Exemplo n.º 1
0
 def test_silent_verbose_on_failure(self, mock_call, caplog, capsys):
     mock_call(stdout='stdout\n', stderr='stderr\n', returncode=1)
     process.call(['ls'], verbose_on_failure=False)
     out, err = capsys.readouterr()
     log_lines = '\n'.join([line[-1] for line in caplog.record_tuples])
     assert 'Running command: ' in log_lines
     assert 'ls' in log_lines
     assert 'stderr' in log_lines
     assert out == ''
Exemplo n.º 2
0
 def test_stderr_terminal_and_logfile_off(self, mock_call, caplog, capsys):
     mock_call(stdout='stdout\n', stderr='some stderr message\n')
     process.call(['ls'], terminal_verbose=False)
     out, err = capsys.readouterr()
     log_lines = [line[-1] for line in caplog.record_tuples]
     assert 'Running command: ' in log_lines[0]
     assert 'ls' in log_lines[0]
     assert 'stderr some stderr message' in log_lines[-1]
     assert out == ''
Exemplo n.º 3
0
def osd_mkfs_bluestore(osd_id, fsid, keyring=None, wal=False, db=False):
    """
    Create the files for the OSD to function. A normal call will look like:

          ceph-osd --cluster ceph --mkfs --mkkey -i 0 \
                   --monmap /var/lib/ceph/osd/ceph-0/activate.monmap \
                   --osd-data /var/lib/ceph/osd/ceph-0 \
                   --osd-uuid 8d208665-89ae-4733-8888-5d3bfbeeec6c \
                   --keyring /var/lib/ceph/osd/ceph-0/keyring \
                   --setuser ceph --setgroup ceph

    In some cases it is required to use the keyring, when it is passed in as
    a keywork argument it is used as part of the ceph-osd command
    """
    path = '/var/lib/ceph/osd/%s-%s/' % (conf.cluster, osd_id)
    monmap = os.path.join(path, 'activate.monmap')

    system.chown(path)

    base_command = [
        'ceph-osd',
        '--cluster', conf.cluster,
        # undocumented flag, sets the `type` file to contain 'bluestore'
        '--osd-objectstore', 'bluestore',
        '--mkfs',
        '-i', osd_id,
        '--monmap', monmap,
    ]

    supplementary_command = [
        '--osd-data', path,
        '--osd-uuid', fsid,
        '--setuser', 'ceph',
        '--setgroup', 'ceph'
    ]

    if keyring is not None:
        base_command.extend(['--keyfile', '-'])

    if wal:
        base_command.extend(
            ['--bluestore-block-wal-path', wal]
        )
        system.chown(wal)

    if db:
        base_command.extend(
            ['--bluestore-block-db-path', db]
        )
        system.chown(db)

    command = base_command + supplementary_command

    process.call(command, stdin=keyring, show_command=True)
Exemplo n.º 4
0
Arquivo: lvm.py Projeto: LenzGr/ceph
    def set_tag(self, key, value):
        """
        Set the key/value pair as an LVM tag.
        """
        # remove it first if it exists
        self.clear_tag(key)

        process.call(
            [
                'lvchange',
                '--addtag', '%s=%s' % (key, value), self.lv_path
            ]
        )
        self.tags[key] = value
Exemplo n.º 5
0
Arquivo: lvm.py Projeto: markhpc/ceph
def remove_lv(lv):
    """
    Removes a logical volume given it's absolute path.

    Will return True if the lv is successfully removed or
    raises a RuntimeError if the removal fails.

    :param lv: A ``Volume`` object or the path for an LV
    """
    if isinstance(lv, Volume):
        path = lv.lv_path
    else:
        path = lv

    stdout, stderr, returncode = process.call(
        [
            'lvremove',
            '-v',  # verbose
            '-f',  # force it
            path
        ],
        show_command=True,
        terminal_verbose=True,
    )
    if returncode != 0:
        raise RuntimeError("Unable to remove %s" % path)
    return True
Exemplo n.º 6
0
def osd_id_available(osd_id):
    """
    Checks to see if an osd ID exists and if it's available for
    reuse. Returns True if it is, False if it isn't.

    :param osd_id: The osd ID to check
    """
    if osd_id is None:
        return False
    bootstrap_keyring = '/var/lib/ceph/bootstrap-osd/%s.keyring' % conf.cluster
    stdout, stderr, returncode = process.call(
        [
            'ceph',
            '--cluster', conf.cluster,
            '--name', 'client.bootstrap-osd',
            '--keyring', bootstrap_keyring,
            'osd',
            'tree',
            '-f', 'json',
        ],
        show_command=True
    )
    if returncode != 0:
        raise RuntimeError('Unable check if OSD id exists: %s' % osd_id)

    output = json.loads(''.join(stdout).strip())
    osds = output['nodes']
    osd = [osd for osd in osds if str(osd['id']) == str(osd_id)]
    if osd and osd[0].get('status') == "destroyed":
        return True
    return False
Exemplo n.º 7
0
def luks_format(key, device):
    """
    Decrypt (open) an encrypted device, previously prepared with cryptsetup

    :param key: dmcrypt secret key, will be used for decrypting
    :param device: Absolute path to device
    """
    command = [
        'cryptsetup',
        '--batch-mode', # do not prompt
        '--key-file', # misnomer, should be key
        '-',          # because we indicate stdin for the key here
        'luksFormat',
        device,
    ]
    process.call(command, stdin=key, terminal_verbose=True, show_command=True)
Exemplo n.º 8
0
Arquivo: disk.py Projeto: markhpc/ceph
def blkid(device):
    """
    The blkid interface to its CLI, creating an output similar to what is
    expected from ``lsblk``. In most cases, ``lsblk()`` should be the preferred
    method for extracting information about a device. There are some corner
    cases where it might provide information that is otherwise unavailable.

    The system call uses the ``-p`` flag which bypasses the cache, the caveat
    being that the keys produced are named completely different to expected
    names.

    For example, instead of ``PARTLABEL`` it provides a ``PART_ENTRY_NAME``.
    A bit of translation between these known keys is done, which is why
    ``lsblk`` should always be preferred: the output provided here is not as
    rich, given that a translation of keys is required for a uniform interface
    with the ``-p`` flag.

    Label name to expected output chart:

    cache bypass name               expected name

    UUID                            UUID
    TYPE                            TYPE
    PART_ENTRY_NAME                 PARTLABEL
    PART_ENTRY_UUID                 PARTUUID
    """
    out, err, rc = process.call(
        ['blkid', '-p', device]
    )
    return _blkid_parser(' '.join(out))
Exemplo n.º 9
0
Arquivo: disk.py Projeto: markhpc/ceph
def _udevadm_info(device):
    """
    Call udevadm and return the output
    """
    cmd = ['udevadm', 'info', '--query=property', device]
    out, _err, _rc = process.call(cmd)
    return out
Exemplo n.º 10
0
def create_id(fsid, json_secrets, osd_id=None):
    """
    :param fsid: The osd fsid to create, always required
    :param json_secrets: a json-ready object with whatever secrets are wanted
                         to be passed to the monitor
    :param osd_id: Reuse an existing ID from an OSD that's been destroyed, if the
                   id does not exist in the cluster a new ID will be created
    """
    bootstrap_keyring = '/var/lib/ceph/bootstrap-osd/%s.keyring' % conf.cluster
    cmd = [
        'ceph',
        '--cluster', conf.cluster,
        '--name', 'client.bootstrap-osd',
        '--keyring', bootstrap_keyring,
        '-i', '-',
        'osd', 'new', fsid
    ]
    if check_id(osd_id):
        cmd.append(osd_id)
    stdout, stderr, returncode = process.call(
        cmd,
        stdin=json_secrets,
        show_command=True
    )
    if returncode != 0:
        raise RuntimeError('Unable to create a new OSD id')
    return ' '.join(stdout).strip()
Exemplo n.º 11
0
def check_id(osd_id):
    """
    Checks to see if an osd ID exists or not. Returns True
    if it does exist, False if it doesn't.

    :param osd_id: The osd ID to check
    """
    if osd_id is None:
        return False
    bootstrap_keyring = '/var/lib/ceph/bootstrap-osd/%s.keyring' % conf.cluster
    stdout, stderr, returncode = process.call(
        [
            'ceph',
            '--cluster', conf.cluster,
            '--name', 'client.bootstrap-osd',
            '--keyring', bootstrap_keyring,
            'osd',
            'tree',
            '-f', 'json',
        ],
        show_command=True
    )
    if returncode != 0:
        raise RuntimeError('Unable check if OSD id exists: %s' % osd_id)

    output = json.loads(''.join(stdout).strip())
    osds = output['nodes']
    return any([str(osd['id']) == str(osd_id) for osd in osds])
Exemplo n.º 12
0
def create_key():
    stdout, stderr, returncode = process.call(
        ['ceph-authtool', '--gen-print-key'],
        show_command=True)
    if returncode != 0:
        raise RuntimeError('Unable to generate a new auth key')
    return ' '.join(stdout).strip()
Exemplo n.º 13
0
def get_dmcrypt_key(osd_id, osd_fsid, lockbox_keyring=None):
    """
    Retrieve the dmcrypt (secret) key stored initially on the monitor. The key
    is sent initially with JSON, and the Monitor then mangles the name to
    ``dm-crypt/osd/<fsid>/luks``

    The ``lockbox.keyring`` file is required for this operation, and it is
    assumed it will exist on the path for the same OSD that is being activated.
    To support scanning, it is optionally configurable to a custom location
    (e.g. inside a lockbox partition mounted in a temporary location)
    """
    if lockbox_keyring is None:
        lockbox_keyring = '/var/lib/ceph/osd/%s-%s/lockbox.keyring' % (conf.cluster, osd_id)
    name = 'client.osd-lockbox.%s' % osd_fsid
    config_key = 'dm-crypt/osd/%s/luks' % osd_fsid

    stdout, stderr, returncode = process.call(
        [
            'ceph',
            '--cluster', conf.cluster,
            '--name', name,
            '--keyring', lockbox_keyring,
            'config-key',
            'get',
            config_key
        ],
        show_command=True
    )
    if returncode != 0:
        raise RuntimeError('Unable to retrieve dmcrypt secret')
    return ' '.join(stdout).strip()
Exemplo n.º 14
0
Arquivo: disk.py Projeto: markhpc/ceph
def get_device_from_partuuid(partuuid):
    """
    If a device has a partuuid, query blkid so that it can tell us what that
    device is
    """
    out, err, rc = process.call(
        ['blkid', '-t', 'PARTUUID="%s"' % partuuid, '-o', 'device']
    )
    return ' '.join(out).strip()
Exemplo n.º 15
0
Arquivo: lvm.py Projeto: ukernel/ceph
    def set_tag(self, key, value):
        """
        Set the key/value pair as an LVM tag. Does not "refresh" the values of
        the current object for its tags. Meant to be a "fire and forget" type
        of modification.
        """
        # remove it first if it exists
        if self.tags.get(key):
            current_value = self.tags[key]
            tag = "%s=%s" % (key, current_value)
            process.call(['lvchange', '--deltag', tag, self.lv_api['lv_path']])

        process.call(
            [
                'lvchange',
                '--addtag', '%s=%s' % (key, value), self.lv_path
            ]
        )
Exemplo n.º 16
0
def _lsblk_type(device):
    """
    Helper function that will use the ``TYPE`` label output of ``lsblk`` to determine
    if a device is a partition or disk.
    It does not process the output to return a boolean, but it does process it to return the
    """
    out, err, rc = process.call(
        ['sudo', 'blkid', '-s', 'PARTUUID', '-o', 'value', device]
    )
    return ' '.join(out).strip()
Exemplo n.º 17
0
Arquivo: disk.py Projeto: markhpc/ceph
def get_partuuid(device):
    """
    If a device is a partition, it will probably have a PARTUUID on it that
    will persist and can be queried against `blkid` later to detect the actual
    device
    """
    out, err, rc = process.call(
        ['blkid', '-s', 'PARTUUID', '-o', 'value', device]
    )
    return ' '.join(out).strip()
Exemplo n.º 18
0
def luks_open(key, device, mapping):
    """
    Decrypt (open) an encrypted device, previously prepared with cryptsetup

    .. note: ceph-disk will require an additional b64decode call for this to work

    :param key: dmcrypt secret key
    :param device: absolute path to device
    :param mapping: mapping name used to correlate device. Usually a UUID
    """
    command = [
        'cryptsetup',
        '--key-file',
        '-',
        'luksOpen',
        device,
        mapping,
    ]
    process.call(command, stdin=key, terminal_verbose=True, show_command=True)
Exemplo n.º 19
0
Arquivo: disk.py Projeto: markhpc/ceph
def get_part_entry_type(device):
    """
    Parses the ``ID_PART_ENTRY_TYPE`` from the "low level" (bypasses the cache)
    output that uses the ``udev`` type of output. This output is intended to be
    used for udev rules, but it is useful in this case as it is the only
    consistent way to retrieve the GUID used by ceph-disk to identify devices.
    """
    out, err, rc = process.call(['blkid', '-p', '-o', 'udev', device])
    for line in out:
        if 'ID_PART_ENTRY_TYPE=' in line:
            return line.split('=')[-1].strip()
    return ''
Exemplo n.º 20
0
def plain_open(key, device, mapping):
    """
    Decrypt (open) an encrypted device, previously prepared with cryptsetup in plain mode

    .. note: ceph-disk will require an additional b64decode call for this to work

    :param key: dmcrypt secret key
    :param device: absolute path to device
    :param mapping: mapping name used to correlate device. Usually a UUID
    """
    command = [
        'cryptsetup',
        '--key-file',
        '-',
        '--allow-discards',  # allow discards (aka TRIM) requests for device
        'open',
        device,
        mapping,
        '--type', 'plain',
        '--key-size', '256',
    ]

    process.call(command, stdin=key, terminal_verbose=True, show_command=True)
Exemplo n.º 21
0
Arquivo: lvm.py Projeto: C2python/ceph
def dmsetup_splitname(dev):
    """
    Run ``dmsetup splitname`` and parse the results.

    .. warning:: This call does not ensure that the device is correct or that
    it exists. ``dmsetup`` will happily take a non existing path and still
    return a 0 exit status.
    """
    command = [
        'dmsetup', 'splitname', '--noheadings',
        "--separator=';'", '--nameprefixes', dev
    ]
    out, err, rc = process.call(command)
    return _splitname_parser(out)
Exemplo n.º 22
0
Arquivo: lvm.py Projeto: ukernel/ceph
    def set_tag(self, key, value):
        """
        Set the key/value pair as an LVM tag. Does not "refresh" the values of
        the current object for its tags. Meant to be a "fire and forget" type
        of modification.

        **warning**: Altering tags on a PV has to be done ensuring that the
        device is actually the one intended. ``pv_name`` is *not* a persistent
        value, only ``pv_uuid`` is. Using ``pv_uuid`` is the best way to make
        sure the device getting changed is the one needed.
        """
        # remove it first if it exists
        if self.tags.get(key):
            current_value = self.tags[key]
            tag = "%s=%s" % (key, current_value)
            process.call(['pvchange', '--deltag', tag, self.pv_name])

        process.call(
            [
                'pvchange',
                '--addtag', '%s=%s' % (key, value), self.pv_name
            ]
        )
Exemplo n.º 23
0
Arquivo: disk.py Projeto: markhpc/ceph
def device_family(device):
    """
    Returns a list of associated devices. It assumes that ``device`` is
    a parent device. It is up to the caller to ensure that the device being
    used is a parent, not a partition.
    """
    labels = ['NAME', 'PARTLABEL', 'TYPE']
    command = ['lsblk', '-P', '-p', '-o', ','.join(labels), device]
    out, err, rc = process.call(command)
    devices = []
    for line in out:
        devices.append(_lsblk_parser(line))

    return devices
Exemplo n.º 24
0
def osd_mkfs_filestore(osd_id, fsid, keyring):
    """
    Create the files for the OSD to function. A normal call will look like:

          ceph-osd --cluster ceph --mkfs --mkkey -i 0 \
                   --monmap /var/lib/ceph/osd/ceph-0/activate.monmap \
                   --osd-data /var/lib/ceph/osd/ceph-0 \
                   --osd-journal /var/lib/ceph/osd/ceph-0/journal \
                   --osd-uuid 8d208665-89ae-4733-8888-5d3bfbeeec6c \
                   --keyring /var/lib/ceph/osd/ceph-0/keyring \
                   --setuser ceph --setgroup ceph

    """
    path = '/var/lib/ceph/osd/%s-%s/' % (conf.cluster, osd_id)
    monmap = os.path.join(path, 'activate.monmap')
    journal = os.path.join(path, 'journal')

    system.chown(journal)
    system.chown(path)

    command = [
        'ceph-osd',
        '--cluster', conf.cluster,
        # undocumented flag, sets the `type` file to contain 'filestore'
        '--osd-objectstore', 'filestore',
        '--mkfs',
        '-i', osd_id,
        '--monmap', monmap,
        '--keyfile', '-', # goes through stdin
        '--osd-data', path,
        '--osd-journal', journal,
        '--osd-uuid', fsid,
        '--setuser', 'ceph',
        '--setgroup', 'ceph'
    ]
    process.call(command, stdin=keyring, terminal_verbose=True, show_command=True)
Exemplo n.º 25
0
def osd_mkfs_filestore(osd_id, fsid, keyring):
    """
    Create the files for the OSD to function. A normal call will look like:

          ceph-osd --cluster ceph --mkfs --mkkey -i 0 \
                   --monmap /var/lib/ceph/osd/ceph-0/activate.monmap \
                   --osd-data /var/lib/ceph/osd/ceph-0 \
                   --osd-journal /var/lib/ceph/osd/ceph-0/journal \
                   --osd-uuid 8d208665-89ae-4733-8888-5d3bfbeeec6c \
                   --keyring /var/lib/ceph/osd/ceph-0/keyring \
                   --setuser ceph --setgroup ceph

    """
    path = '/var/lib/ceph/osd/%s-%s/' % (conf.cluster, osd_id)
    monmap = os.path.join(path, 'activate.monmap')
    journal = os.path.join(path, 'journal')

    system.chown(journal)
    system.chown(path)

    command = [
        'ceph-osd',
        '--cluster', conf.cluster,
        '--osd-objectstore', 'filestore',
        '--mkfs',
        '-i', osd_id,
        '--monmap', monmap,
    ]

    if __release__ != 'luminous':
        # goes through stdin
        command.extend(['--keyfile', '-'])

    command.extend([
        '--osd-data', path,
        '--osd-journal', journal,
        '--osd-uuid', fsid,
        '--setuser', 'ceph',
        '--setgroup', 'ceph'
    ])

    _, _, returncode = process.call(
        command, stdin=keyring, terminal_verbose=True, show_command=True
    )
    if returncode != 0:
        raise RuntimeError('Command failed with exit code %s: %s' % (returncode, ' '.join(command)))
Exemplo n.º 26
0
Arquivo: lvm.py Projeto: ukernel/ceph
def get_api_lvs():
    """
    Return the list of logical volumes available in the system using flags to include common
    metadata associated with them

    Command and delimeted output, should look like::

        $ lvs --noheadings --readonly --separator=';' -o lv_tags,lv_path,lv_name,vg_name
          ;/dev/ubuntubox-vg/root;root;ubuntubox-vg
          ;/dev/ubuntubox-vg/swap_1;swap_1;ubuntubox-vg

    """
    fields = 'lv_tags,lv_path,lv_name,vg_name,lv_uuid'
    stdout, stderr, returncode = process.call(
        ['lvs', '--noheadings', '--readonly', '--separator=";"', '-o', fields]
    )
    return _output_parser(stdout, fields)
Exemplo n.º 27
0
Arquivo: api.py Projeto: Carudy/ceph
def get_api_vgs():
    """
    Return the list of group volumes available in the system using flags to include common
    metadata associated with them

    Command and sample JSON output, should look like::

        $ sudo vgs --reportformat=json
        {
            "report": [
                {
                    "vg": [
                        {
                            "vg_name":"VolGroup00",
                            "pv_count":"1",
                            "lv_count":"2",
                            "snap_count":"0",
                            "vg_attr":"wz--n-",
                            "vg_size":"38.97g",
                            "vg_free":"0 "},
                        {
                            "vg_name":"osd_vg",
                            "pv_count":"3",
                            "lv_count":"1",
                            "snap_count":"0",
                            "vg_attr":"wz--n-",
                            "vg_size":"32.21g",
                            "vg_free":"9.21g"
                        }
                    ]
                }
            ]
        }

    """
    stdout, stderr, returncode = process.call(
        [
            'sudo', 'vgs', '--reportformat=json'
        ]
    )
    report = json.loads(''.join(stdout))
    for report_item in report.get('report', []):
        # is it possible to get more than one item in "report" ?
        return report_item['vg']
    return []
Exemplo n.º 28
0
Arquivo: lvm.py Projeto: ukernel/ceph
def get_api_vgs():
    """
    Return the list of group volumes available in the system using flags to
    include common metadata associated with them

    Command and sample delimeted output, should look like::

        $ vgs --noheadings --readonly --separator=';' \
          -o vg_name,pv_count,lv_count,snap_count,vg_attr,vg_size,vg_free
          ubuntubox-vg;1;2;0;wz--n-;299.52g;12.00m
          osd_vg;3;1;0;wz--n-;29.21g;9.21g

    """
    fields = 'vg_name,pv_count,lv_count,snap_count,vg_attr,vg_size,vg_free'
    stdout, stderr, returncode = process.call(
        ['vgs', '--noheadings', '--readonly', '--separator=";"', '-o', fields]
    )
    return _output_parser(stdout, fields)
Exemplo n.º 29
0
def status(device):
    """
    Capture the metadata information of a possibly encrypted device, returning
    a dictionary with all the values found (if any).

    An encrypted device will contain information about a device. Example
    successful output looks like::

        $ cryptsetup status /dev/mapper/ed6b5a26-eafe-4cd4-87e3-422ff61e26c4
        /dev/mapper/ed6b5a26-eafe-4cd4-87e3-422ff61e26c4 is active and is in use.
          type:    LUKS1
          cipher:  aes-xts-plain64
          keysize: 256 bits
          device:  /dev/sdc2
          offset:  4096 sectors
          size:    20740063 sectors
          mode:    read/write

    As long as the mapper device is in 'open' state, the ``status`` call will work.

    :param device: Absolute path or UUID of the device mapper
    """
    command = [
        'cryptsetup',
        'status',
        device,
    ]
    out, err, code = process.call(command, show_command=True, verbose_on_failure=False)

    metadata = {}
    if code != 0:
        logger.warning('failed to detect device mapper information')
        return metadata
    for line in out:
        # get rid of lines that might not be useful to construct the report:
        if not line.startswith(' '):
            continue
        try:
            column, value = line.split(': ')
        except ValueError:
            continue
        metadata[column.strip()] = value.strip().strip('"')
    return metadata
Exemplo n.º 30
0
Arquivo: lvm.py Projeto: ukernel/ceph
def remove_lv(path):
    """
    Removes a logical volume given it's absolute path.

    Will return True if the lv is successfully removed or
    raises a RuntimeError if the removal fails.
    """
    stdout, stderr, returncode = process.call(
        [
            'lvremove',
            '-v',  # verbose
            '-f',  # force it
            path
        ],
        show_command=True,
        terminal_verbose=True,
    )
    if returncode != 0:
        raise RuntimeError("Unable to remove %s".format(path))
    return True
Exemplo n.º 31
0
def get_pvs(fields=PV_FIELDS, filters='', tags=None):
    """
    Return a list of PVs that are available on the system and match the
    filters and tags passed. Argument filters takes a dictionary containing
    arguments required by -S option of LVM. Passing a list of LVM tags can be
    quite tricky to pass as a dictionary within dictionary, therefore pass
    dictionary of tags via tags argument and tricky part will be taken care of
    by the helper methods.

    :param fields: string containing list of fields to be displayed by the
                   pvs command
    :param sep: string containing separator to be used between two fields
    :param filters: dictionary containing LVM filters
    :param tags: dictionary containng LVM tags
    :returns: list of class PVolume object representing pvs on the system
    """
    filters = make_filters_lvmcmd_ready(filters, tags)
    args = ['pvs', '--no-heading', '--readonly', '--separator=";"', '-S',
            filters, '-o', fields]

    stdout, stderr, returncode = process.call(args, verbose_on_failure=False)
    pvs_report = _output_parser(stdout, fields)
    return [PVolume(**pv_report) for pv_report in pvs_report]
Exemplo n.º 32
0
def get_vgs(fields=VG_FIELDS, filters='', tags=None):
    """
    Return a list of VGs that are available on the system and match the
    filters and tags passed. Argument filters takes a dictionary containing
    arguments required by -S option of LVM. Passing a list of LVM tags can be
    quite tricky to pass as a dictionary within dictionary, therefore pass
    dictionary of tags via tags argument and tricky part will be taken care of
    by the helper methods.

    :param fields: string containing list of fields to be displayed by the
                   vgs command
    :param sep: string containing separator to be used between two fields
    :param filters: dictionary containing LVM filters
    :param tags: dictionary containng LVM tags
    :returns: list of class VolumeGroup object representing vgs on the system
    """
    filters = make_filters_lvmcmd_ready(filters, tags)
    args = ['vgs'] + VG_CMD_OPTIONS + ['-S', filters, '-o', fields]

    stdout, stderr, returncode = process.call(args,
                                              run_on_host=True,
                                              verbose_on_failure=False)
    vgs_report = _output_parser(stdout, fields)
    return [VolumeGroup(**vg_report) for vg_report in vgs_report]
Exemplo n.º 33
0
def _validate_bluestore_device(device, excepted_device_type, osd_uuid):
    """
    Validate whether the given device is truly what it is supposed to be
    """

    out, err, ret = process.call(['ceph-bluestore-tool', 'show-label', '--dev', device])
    if err:
        terminal.error('ceph-bluestore-tool failed to run. %s'% err)
        raise SystemExit(1)
    if ret:
        terminal.error('no label on %s'% device)
        raise SystemExit(1)
    oj = json.loads(''.join(out))
    if device not in oj:
        terminal.error('%s not in the output of ceph-bluestore-tool, buggy?'% device)
        raise SystemExit(1)
    current_device_type = oj[device]['description']
    if current_device_type != excepted_device_type:
        terminal.error('%s is not a %s device but %s'% (device, excepted_device_type, current_device_type))
        raise SystemExit(1)
    current_osd_uuid = oj[device]['osd_uuid']
    if current_osd_uuid != osd_uuid:
        terminal.error('device %s is used by another osd %s as %s, should be %s'% (device, current_osd_uuid, current_device_type, osd_uuid))
        raise SystemExit(1)
Exemplo n.º 34
0
 def test_stdin(self):
     process.call(['xargs', 'ls'], stdin="echo '/'")
Exemplo n.º 35
0
def lsblk_all(device='', columns=None, abspath=False):
    """
    Create a dictionary of identifying values for a device using ``lsblk``.
    Each supported column is a key, in its *raw* format (all uppercase
    usually).  ``lsblk`` has support for certain "columns" (in blkid these
    would be labels), and these columns vary between distributions and
    ``lsblk`` versions. The newer versions support a richer set of columns,
    while older ones were a bit limited.

    These are a subset of lsblk columns which are known to work on both CentOS 7 and Xenial:

         NAME  device name
        KNAME  internal kernel device name
        PKNAME internal kernel parent device name
      MAJ:MIN  major:minor device number
       FSTYPE  filesystem type
   MOUNTPOINT  where the device is mounted
        LABEL  filesystem LABEL
         UUID  filesystem UUID
           RO  read-only device
           RM  removable device
        MODEL  device identifier
         SIZE  size of the device
        STATE  state of the device
        OWNER  user name
        GROUP  group name
         MODE  device node permissions
    ALIGNMENT  alignment offset
       MIN-IO  minimum I/O size
       OPT-IO  optimal I/O size
      PHY-SEC  physical sector size
      LOG-SEC  logical sector size
         ROTA  rotational device
        SCHED  I/O scheduler name
      RQ-SIZE  request queue size
         TYPE  device type
      PKNAME   internal parent kernel device name
     DISC-ALN  discard alignment offset
    DISC-GRAN  discard granularity
     DISC-MAX  discard max bytes
    DISC-ZERO  discard zeroes data

    There is a bug in ``lsblk`` where using all the available (supported)
    columns will result in no output (!), in order to workaround this the
    following columns have been removed from the default reporting columns:

    * RQ-SIZE (request queue size)
    * MIN-IO  minimum I/O size
    * OPT-IO  optimal I/O size

    These should be available however when using `columns`. For example::

        >>> lsblk('/dev/sda1', columns=['OPT-IO'])
        {'OPT-IO': '0'}

    Normal CLI output, as filtered by the flags in this function will look like ::

        $ lsblk -P -o NAME,KNAME,PKNAME,MAJ:MIN,FSTYPE,MOUNTPOINT
        NAME="sda1" KNAME="sda1" MAJ:MIN="8:1" FSTYPE="ext4" MOUNTPOINT="/"

    :param columns: A list of columns to report as keys in its original form.
    :param abspath: Set the flag for absolute paths on the report
    """
    default_columns = [
        'NAME', 'KNAME', 'PKNAME', 'MAJ:MIN', 'FSTYPE', 'MOUNTPOINT', 'LABEL',
        'UUID', 'RO', 'RM', 'MODEL', 'SIZE', 'STATE', 'OWNER', 'GROUP', 'MODE',
        'ALIGNMENT', 'PHY-SEC', 'LOG-SEC', 'ROTA', 'SCHED', 'TYPE', 'DISC-ALN',
        'DISC-GRAN', 'DISC-MAX', 'DISC-ZERO', 'PKNAME', 'PARTLABEL'
    ]
    columns = columns or default_columns
    # -P       -> Produce pairs of COLUMN="value"
    # -p       -> Return full paths to devices, not just the names, when ``abspath`` is set
    # -o       -> Use the columns specified or default ones provided by this function
    base_command = ['lsblk', '-P']
    if abspath:
        base_command.append('-p')
    base_command.append('-o')
    base_command.append(','.join(columns))

    out, err, rc = process.call(base_command)

    if rc != 0:
        raise RuntimeError(f"Error: {err}")

    result = []

    for line in out:
        result.append(_lsblk_parser(line))

    if not device:
        return result

    for dev in result:
        if dev['NAME'] == os.path.basename(device):
            return dev

    return {}
Exemplo n.º 36
0
 def test_unicode_encoding_stdin(self):
     process.call(['echo'], stdin=u'\xd0'.encode('utf-8'))
Exemplo n.º 37
0
 def test_unicode_encoding(self):
     process.call(['echo', u'\xd0'])
Exemplo n.º 38
0
 def clear_tag(self, key):
     if self.tags.get(key):
         current_value = self.tags[key]
         tag = "%s=%s" % (key, current_value)
         process.call(['lvchange', '--deltag', tag, self.lv_path])
         del self.tags[key]
Exemplo n.º 39
0
 def deactivate(self):
     """
     Deactivate the LV by calling lvchange -an
     """
     process.call(['lvchange', '-an', self.lv_path])
Exemplo n.º 40
0
def osd_mkfs_bluestore(osd_id, fsid, keyring=None, wal=False, db=False):
    """
    Create the files for the OSD to function. A normal call will look like:

          ceph-osd --cluster ceph --mkfs --mkkey -i 0 \
                   --monmap /var/lib/ceph/osd/ceph-0/activate.monmap \
                   --osd-data /var/lib/ceph/osd/ceph-0 \
                   --osd-uuid 8d208665-89ae-4733-8888-5d3bfbeeec6c \
                   --keyring /var/lib/ceph/osd/ceph-0/keyring \
                   --setuser ceph --setgroup ceph

    In some cases it is required to use the keyring, when it is passed in as
    a keyword argument it is used as part of the ceph-osd command
    """
    path = '/var/lib/ceph/osd/%s-%s/' % (conf.cluster, osd_id)
    monmap = os.path.join(path, 'activate.monmap')

    system.chown(path)

    base_command = [
        'ceph-osd',
        '--cluster',
        conf.cluster,
        '--osd-objectstore',
        'bluestore',
        '--mkfs',
        '-i',
        osd_id,
        '--monmap',
        monmap,
    ]

    supplementary_command = [
        '--osd-data', path, '--osd-uuid', fsid, '--setuser', 'ceph',
        '--setgroup', 'ceph'
    ]

    if keyring is not None:
        base_command.extend(['--keyfile', '-'])

    if wal:
        base_command.extend(['--bluestore-block-wal-path', wal])
        system.chown(wal)

    if db:
        base_command.extend(['--bluestore-block-db-path', db])
        system.chown(db)

    if get_osdspec_affinity():
        base_command.extend(['--osdspec-affinity', get_osdspec_affinity()])

    command = base_command + supplementary_command
    """
    When running in containers the --mkfs on raw device sometimes fails
    to acquire a lock through flock() on the device because systemd-udevd holds one temporarily.
    See KernelDevice.cc and _lock() to understand how ceph-osd acquires the lock.
    Because this is really transient, we retry up to 5 times and wait for 1 sec in-between
    """
    for retry in range(5):
        _, _, returncode = process.call(command,
                                        stdin=keyring,
                                        terminal_verbose=True,
                                        show_command=True)
        if returncode == 0:
            break
        else:
            if returncode == errno.EWOULDBLOCK:
                time.sleep(1)
                logger.info(
                    'disk is held by another process, trying to mkfs again... (%s/5 attempt)'
                    % retry)
                continue
            else:
                raise RuntimeError('Command failed with exit code %s: %s' %
                                   (returncode, ' '.join(command)))
Exemplo n.º 41
0
 def generate(self, devs=None):
     if not devs:
         logger.debug('Listing block devices via lsblk...')
         devs = []
         # adding '--inverse' allows us to get the mapper devices list in that command output.
         # not listing root devices containing partitions shouldn't have side effect since we are
         # in `ceph-volume raw` context.
         #
         #   example:
         #   running `lsblk --paths --nodeps --output=NAME --noheadings` doesn't allow to get the mapper list
         #   because the output is like following :
         #
         #   $ lsblk --paths --nodeps --output=NAME --noheadings
         #   /dev/sda
         #   /dev/sdb
         #   /dev/sdc
         #   /dev/sdd
         #
         #   the dmcrypt mappers are hidden because of the `--nodeps` given they are displayed as a dependency.
         #
         #   $ lsblk --paths --output=NAME --noheadings
         #   /dev/sda
         #   |-/dev/mapper/ceph-3b52c90d-6548-407d-bde1-efd31809702f-sda-block-dmcrypt
         #   `-/dev/mapper/ceph-3b52c90d-6548-407d-bde1-efd31809702f-sda-db-dmcrypt
         #   /dev/sdb
         #   /dev/sdc
         #   /dev/sdd
         #
         #   adding `--inverse` is a trick to get around this issue, the counterpart is that we can't list root devices if they contain
         #   at least one partition but this shouldn't be an issue in `ceph-volume raw` context given we only deal with raw devices.
         out, err, ret = process.call([
             'lsblk', '--paths', '--nodeps', '--output=NAME',
             '--noheadings', '--inverse'
         ])
         assert not ret
         devs = out
     result = {}
     for dev in devs:
         logger.debug('Examining %s' % dev)
         # bluestore?
         out, err, ret = process.call(
             ['ceph-bluestore-tool', 'show-label', '--dev', dev],
             verbose_on_failure=False)
         if ret:
             logger.debug('No label on %s' % dev)
             continue
         oj = json.loads(''.join(out))
         if dev not in oj:
             continue
         if oj[dev]['description'] != 'main':
             # ignore non-main devices, for now
             continue
         whoami = oj[dev]['whoami']
         result[whoami] = {
             'type': 'bluestore',
             'osd_id': int(whoami),
         }
         for f in ['osd_uuid', 'ceph_fsid']:
             result[whoami][f] = oj[dev][f]
         result[whoami]['device'] = dev
     return result
Exemplo n.º 42
0
def get_device_vgs(device, name_prefix=''):
    stdout, stderr, returncode = process.call(['pvs'] + VG_CMD_OPTIONS +
                                              ['-o', VG_FIELDS, device],
                                              verbose_on_failure=False)
    vgs = _output_parser(stdout, VG_FIELDS)
    return [VolumeGroup(**vg) for vg in vgs]
Exemplo n.º 43
0
def get_device_lvs(device, name_prefix=''):
    stdout, stderr, returncode = process.call(['pvs'] + LV_CMD_OPTIONS +
                                              ['-o', LV_FIELDS, device],
                                              verbose_on_failure=False)
    lvs = _output_parser(stdout, LV_FIELDS)
    return [Volume(**lv) for lv in lvs]
Exemplo n.º 44
0
def lsblk(device, columns=None):
    """
    Create a dictionary of identifying values for a device using ``lsblk``.
    Each supported column is a key, in its *raw* format (all uppercase
    usually).  ``lsblk`` has support for certain "columns" (in blkid these
    would be labels), and these columns vary between distributions and
    ``lsblk`` versions. The newer versions support a richer set of columns,
    while older ones were a bit limited.

    These are the default lsblk columns reported which are safe to use for
    Ubuntu 14.04.5 LTS:

         NAME  device name
        KNAME  internal kernel device name
      MAJ:MIN  major:minor device number
       FSTYPE  filesystem type
   MOUNTPOINT  where the device is mounted
        LABEL  filesystem LABEL
         UUID  filesystem UUID
           RO  read-only device
           RM  removable device
        MODEL  device identifier
         SIZE  size of the device
        STATE  state of the device
        OWNER  user name
        GROUP  group name
         MODE  device node permissions
    ALIGNMENT  alignment offset
       MIN-IO  minimum I/O size
       OPT-IO  optimal I/O size
      PHY-SEC  physical sector size
      LOG-SEC  logical sector size
         ROTA  rotational device
        SCHED  I/O scheduler name
      RQ-SIZE  request queue size
         TYPE  device type
     DISC-ALN  discard alignment offset
    DISC-GRAN  discard granularity
     DISC-MAX  discard max bytes
    DISC-ZERO  discard zeroes data

    There is a bug in ``lsblk`` where using all the available (supported)
    columns will result in no output (!), in order to workaround this the
    following columns have been removed from the default reporting columns:

    * RQ-SIZE (request queue size)
    * MIN-IO  minimum I/O size
    * OPT-IO  optimal I/O size

    These should be available however when using `columns`. For example::

        >>> lsblk('/dev/sda1', columns=['OPT-IO'])
        {'OPT-IO': '0'}

    Normal CLI output, as filtered by the flags in this function will look like ::

        $ sudo lsblk --nodeps -P -o NAME,KNAME,MAJ:MIN,FSTYPE,MOUNTPOINT
        NAME="sda1" KNAME="sda1" MAJ:MIN="8:1" FSTYPE="ext4" MOUNTPOINT="/"

    :param columns: A list of columns to report as keys in its original form.
    """
    default_columns = [
        'NAME', 'KNAME', 'MAJ:MIN', 'FSTYPE', 'MOUNTPOINT', 'LABEL', 'UUID',
        'RO', 'RM', 'MODEL', 'SIZE', 'STATE', 'OWNER', 'GROUP', 'MODE',
        'ALIGNMENT', 'PHY-SEC', 'LOG-SEC', 'ROTA', 'SCHED', 'TYPE', 'DISC-ALN',
        'DISC-GRAN', 'DISC-MAX', 'DISC-ZERO'
    ]
    device = device.rstrip('/')
    columns = columns or default_columns
    # --nodeps -> Avoid adding children/parents to the device, only give information
    #             on the actual device we are querying for
    # -P       -> Produce pairs of COLUMN="value"
    # -o       -> Use the columns specified or default ones provided by this function
    command = ['sudo', 'lsblk', '--nodeps', '-P', '-o']
    command.append(','.join(columns))
    command.append(device)
    out, err, rc = process.call(command)

    if rc != 0:
        return {}

    # parse the COLUMN="value" output to construct the dictionary
    pairs = ' '.join(out).split()
    parsed = {}
    for pair in pairs:
        try:
            column, value = pair.split('=')
        except ValueError:
            continue
        parsed[column] = value.strip().strip().strip('"')
    return parsed
Exemplo n.º 45
0
def is_active(unit):
    out, err, rc = process.call(['systemctl', 'is-active', unit],
                                verbose_on_failure=False)
    return rc == 0
Exemplo n.º 46
0
    def generate(self, devs=None):
        logger.debug('Listing block devices via lsblk...')
        # in case where we come from `ceph-volume raw activate`
        # `--device` will call `List.list()` with a string instead of a list
        # which can lead the logic in this function to a bug. The following lines will basically
        # convert it to a list with a single element to be sure we don't hit any issue.
        if isinstance(devs, str):
            devs = [devs]
        if devs is None or devs == []:
            devs = []
            # If no devs are given initially, we want to list ALL devices including children and
            # parents. Parent disks with child partitions may be the appropriate device to return if
            # the parent disk has a bluestore header, but children may be the most appropriate
            # devices to return if the parent disk does not have a bluestore header.
            out, err, ret = process.call([
                'lsblk', '--paths', '--output=NAME', '--noheadings', '--list'
            ])
            assert not ret
            devs = out

        result = {}
        logger.debug('inspecting devices: {}'.format(devs))
        for dev in devs:
            info = disk.lsblk(dev, abspath=True)
            # Linux kernels built with CONFIG_ATARI_PARTITION enabled can falsely interpret
            # bluestore's on-disk format as an Atari partition table. These false Atari partitions
            # can be interpreted as real OSDs if a bluestore OSD was previously created on the false
            # partition. See https://tracker.ceph.com/issues/52060 for more info. If a device has a
            # parent, it is a child. If the parent is a valid bluestore OSD, the child will only
            # exist if it is a phantom Atari partition, and the child should be ignored. If the
            # parent isn't bluestore, then the child could be a valid bluestore OSD. If we fail to
            # determine whether a parent is bluestore, we should err on the side of not reporting
            # the child so as not to give a false negative.
            if 'PKNAME' in info and info['PKNAME'] != "":
                parent = info['PKNAME']
                try:
                    if disk.has_bluestore_label(parent):
                        logger.warning((
                            'ignoring child device {} whose parent {} is a BlueStore OSD.'
                            .format(dev, parent),
                            'device is likely a phantom Atari partition. device info: {}'
                            .format(info)))
                        continue
                except OSError as e:
                    logger.error((
                        'ignoring child device {} to avoid reporting invalid BlueStore data from phantom Atari partitions.'
                        .format(dev),
                        'failed to determine if parent device {} is BlueStore. err: {}'
                        .format(parent, e)))
                    continue

            bs_info = _get_bluestore_info(dev)
            if bs_info is None:
                # None is also returned in the rare event that there is an issue reading info from
                # a BlueStore disk, so be sure to log our assumption that it isn't bluestore
                logger.info(
                    'device {} does not have BlueStore information'.format(
                        dev))
                continue
            result[bs_info['osd_uuid']] = bs_info

        return result
Exemplo n.º 47
0
def create_key():
    stdout, stderr, returncode = process.call(
        ['ceph-authtool', '--gen-print-key'], show_command=True)
    if returncode != 0:
        raise RuntimeError('Unable to generate a new auth key')
    return ' '.join(stdout).strip()
Exemplo n.º 48
0
 def has_bluestore_label(self):
     out, err, ret = process.call(
         ['ceph-bluestore-tool', 'show-label', '--dev', self.path])
     if ret:
         return False
     return True