Exemple #1
0
 def get_ptuuid(self, argument):
     uuid = disk.get_partuuid(argument)
     if not uuid:
         terminal.error('blkid could not detect a PARTUUID for device: %s' %
                        argument)
         raise RuntimeError('unable to use device')
     return uuid
Exemple #2
0
    def main(self):
        sub_command_help = dedent("""
        Create an OSD by assigning an ID and FSID, registering them with the
        cluster with an ID and FSID, formatting and mounting the volume, and
        starting the OSD daemon. This is a convinience command that combines
        the prepare and activate steps.

        Encryption is not supported.

        Separate DB and WAL devices are not supported.

            ceph-volume raw create --data /dev/sdb

        """)
        parser = create_parser(
            prog='ceph-volume raw create',
            description=sub_command_help,
        )
        parser.add_argument(
            '--no-systemd',
            dest='no_systemd',
            action='store_true',
            help='Skip creating and enabling systemd units and starting OSD services',
        )
        if len(self.argv) == 0:
            print(sub_command_help)
            return
        self.args = parser.parse_args(self.argv)
        if not self.args.bluestore:
            terminal.error('must specify --bluestore (currently the only supported backend)')
            raise SystemExit(1)
        if not self.args.no_systemd:
            terminal.error('systemd support not yet implemented')
            raise SystemExit(1)
        self.create(self.args)
Exemple #3
0
    def scan_directory(self, path):
        osd_metadata = {'cluster_name': conf.cluster}
        directory_files = os.listdir(path)
        if 'keyring' not in directory_files:
            raise RuntimeError(
                'OSD files not found, required "keyring" file is not present at: %s'
                % path)
        for file_ in os.listdir(path):
            file_path = os.path.join(path, file_)
            file_json_key = file_
            if file_.endswith('_dmcrypt'):
                file_json_key = file_.rstrip('_dmcrypt')
                logger.info(('reading file {}, stripping _dmcrypt',
                             'suffix').format(file_))
            if os.path.islink(file_path):
                if os.path.exists(file_path):
                    osd_metadata[file_json_key] = self.scan_device(file_path)
                else:
                    msg = 'broken symlink found %s -> %s' % (
                        file_path, os.path.realpath(file_path))
                    terminal.warning(msg)
                    logger.warning(msg)

            if os.path.isdir(file_path):
                continue

            # the check for binary needs to go before the file, to avoid
            # capturing data from binary files but still be able to capture
            # contents from actual files later
            try:
                if system.is_binary(file_path):
                    logger.info('skipping binary file: %s' % file_path)
                    continue
            except IOError:
                logger.exception('skipping due to IOError on file: %s' %
                                 file_path)
                continue
            if os.path.isfile(file_path):
                content = self.get_contents(file_path)
                if 'keyring' in file_path:
                    content = parse_keyring(content)
                try:
                    osd_metadata[file_json_key] = int(content)
                except ValueError:
                    osd_metadata[file_json_key] = content

        # we must scan the paths again because this might be a temporary mount
        path_mounts = system.get_mounts(paths=True)
        device = path_mounts.get(path)

        # it is possible to have more than one device, pick the first one, and
        # warn that it is possible that more than one device is 'data'
        if not device:
            terminal.error('Unable to detect device mounted for path: %s' %
                           path)
            raise RuntimeError('Cannot activate OSD')
        osd_metadata['data'] = self.scan_device(
            device[0] if len(device) else None)

        return osd_metadata
Exemple #4
0
    def zap(self, devices=None):
        devices = devices or self.args.devices

        for device in devices:
            mlogger.info("Zapping: %s", device.abspath)
            if device.is_mapper:
                terminal.error(
                    "Refusing to zap the mapper device: {}".format(device))
                raise SystemExit(1)
            if device.is_lvm_member:
                self.zap_lvm_member(device)
            if device.is_lv:
                self.zap_lv(device)
            if device.is_partition:
                self.zap_partition(device)
            if device.is_device:
                self.zap_raw_device(device)

        if self.args.devices:
            terminal.success("Zapping successful for: %s" %
                             ", ".join([str(d) for d in self.args.devices]))
        else:
            terminal.success(
                "Zapping successful for OSD: %s" % self.args.osd_id
                or self.args.osd_fsid)
Exemple #5
0
def rollback_osd(args, osd_id=None):
    """
    When the process of creating or preparing fails, the OSD needs to be
    destroyed so that the ID cane be reused.  This is prevents leaving the ID
    around as "used" on the monitor, which can cause confusion if expecting
    sequential OSD IDs.

    The usage of `destroy-new` allows this to be done without requiring the
    admin keyring (otherwise needed for destroy and purge commands)
    """
    if not osd_id:
        # it means that it wasn't generated, so there is nothing to rollback here
        return

    # once here, this is an error condition that needs to be rolled back
    terminal.error('Was unable to complete a new OSD, will rollback changes')
    osd_name = 'osd.%s'
    bootstrap_keyring = '/var/lib/ceph/bootstrap-osd/%s.keyring' % conf.cluster
    cmd = [
        'ceph',
        '--cluster', conf.cluster,
        '--name', 'client.bootstrap-osd',
        '--keyring', bootstrap_keyring,
        'osd', 'purge-new', osd_name % osd_id,
        '--yes-i-really-mean-it',
    ]

    process.run(cmd)
Exemple #6
0
def rollback_osd(args, osd_id=None):
    """
    When the process of creating or preparing fails, the OSD needs to be
    destroyed so that the ID cane be reused.  This is prevents leaving the ID
    around as "used" on the monitor, which can cause confusion if expecting
    sequential OSD IDs.

    The usage of `destroy-new` allows this to be done without requiring the
    admin keyring (otherwise needed for destroy and purge commands)
    """
    if not osd_id:
        # it means that it wasn't generated, so there is nothing to rollback here
        return

    # once here, this is an error condition that needs to be rolled back
    terminal.error('Was unable to complete a new OSD, will rollback changes')
    osd_name = 'osd.%s'
    bootstrap_keyring = '/var/lib/ceph/bootstrap-osd/%s.keyring' % conf.cluster
    cmd = [
        'ceph',
        '--cluster', conf.cluster,
        '--name', 'client.bootstrap-osd',
        '--keyring', bootstrap_keyring,
        'osd', 'purge-new', osd_name % osd_id,
        '--yes-i-really-mean-it',
    ]

    process.run(cmd)
Exemple #7
0
    def main(self):
        sub_command_help = dedent("""
        Prepare an OSD by assigning an ID and FSID, registering them with the
        cluster with an ID and FSID, formatting the volume.

        Once the OSD is ready, an ad-hoc systemd unit will be enabled so that
        it can later get activated and the OSD daemon can get started.

        Encryption is not supported.

        DB and WAL devices are not supported.

            ceph-volume raw prepare --bluestore --data {device}

        """)
        parser = create_parser(
            prog='ceph-volume raw prepare',
            description=sub_command_help,
        )
        if len(self.argv) == 0:
            print(sub_command_help)
            return
        self.args = parser.parse_args(self.argv)
        if not self.args.bluestore:
            terminal.error('must specify --bluestore (currently the only supported backend)')
            raise SystemExit(1)

        self.safe_prepare(self.args)
Exemple #8
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)
Exemple #9
0
    def scan_directory(self, path):
        osd_metadata = {'cluster_name': conf.cluster}
        path_mounts = system.get_mounts(paths=True)
        for _file in os.listdir(path):
            file_path = os.path.join(path, _file)
            if os.path.islink(file_path):
                osd_metadata[_file] = self.scan_device(file_path)
            if os.path.isdir(file_path):
                continue
            # the check for binary needs to go before the file, to avoid
            # capturing data from binary files but still be able to capture
            # contents from actual files later
            if system.is_binary(file_path):
                continue
            if os.path.isfile(file_path):
                osd_metadata[_file] = self.get_contents(file_path)

        device = path_mounts.get(path)
        # it is possible to have more than one device, pick the first one, and
        # warn that it is possible that more than one device is 'data'
        if not device:
            terminal.error('Unable to detect device mounted for path: %s' % path)
            raise RuntimeError('Cannot activate OSD')
        osd_metadata['data'] = self.scan_device(device[0] if len(device) else None)

        return osd_metadata
Exemple #10
0
    def scan_directory(self, path):
        osd_metadata = {'cluster_name': conf.cluster}
        path_mounts = system.get_mounts(paths=True)
        for _file in os.listdir(path):
            file_path = os.path.join(path, _file)
            if os.path.islink(file_path):
                osd_metadata[_file] = self.scan_device(file_path)
            if os.path.isdir(file_path):
                continue
            # the check for binary needs to go before the file, to avoid
            # capturing data from binary files but still be able to capture
            # contents from actual files later
            if system.is_binary(file_path):
                continue
            if os.path.isfile(file_path):
                content = self.get_contents(file_path)
                try:
                    osd_metadata[_file] = int(content)
                except ValueError:
                    osd_metadata[_file] = content

        device = path_mounts.get(path)
        # it is possible to have more than one device, pick the first one, and
        # warn that it is possible that more than one device is 'data'
        if not device:
            terminal.error('Unable to detect device mounted for path: %s' % path)
            raise RuntimeError('Cannot activate OSD')
        osd_metadata['data'] = self.scan_device(device[0] if len(device) else None)

        return osd_metadata
Exemple #11
0
    def main(self):
        sub_command_help = dedent("""
        Prepare an OSD by assigning an ID and FSID, registering them with the
        cluster with an ID and FSID, formatting the volume.

        Once the OSD is ready, an ad-hoc systemd unit will be enabled so that
        it can later get activated and the OSD daemon can get started.

            ceph-volume raw prepare --bluestore --data {device}

        DB and WAL devices are supported.

            ceph-volume raw prepare --bluestore --data {device} --block.db {device} --block.wal {device}

        """)
        parser = create_parser(
            prog='ceph-volume raw prepare',
            description=sub_command_help,
        )
        if not self.argv:
            print(sub_command_help)
            return
        self.args = parser.parse_args(self.argv)
        if not self.args.bluestore:
            terminal.error(
                'must specify --bluestore (currently the only supported backend)'
            )
            raise SystemExit(1)
        if self.args.dmcrypt and not os.getenv('CEPH_VOLUME_DMCRYPT_SECRET'):
            terminal.error('encryption was requested (--dmcrypt) but environment variable ' \
                           'CEPH_VOLUME_DMCRYPT_SECRET is not set, you must set ' \
                           'this variable to provide a dmcrypt secret.')
            raise SystemExit(1)

        self.safe_prepare(self.args)
Exemple #12
0
    def main(self):
        if not self.args.devices:
            return self.parser.print_help()

        # Default to bluestore here since defaulting it in add_argument may
        # cause both to be True
        if not self.args.bluestore and not self.args.filestore:
            self.args.bluestore = True

        if (self.args.auto and not self.args.db_devices
                and not self.args.wal_devices
                and not self.args.journal_devices):
            self._sort_rotational_disks()

        self._check_slot_args()

        ensure_disjoint_device_lists(self.args.devices, self.args.db_devices,
                                     self.args.wal_devices,
                                     self.args.journal_devices)

        plan = self.get_plan(self.args)

        if self.args.report:
            self.report(plan)
            return 0

        if not self.args.yes:
            self.report(plan)
            terminal.info(
                'The above OSDs would be created if the operation continues')
            if not prompt_bool('do you want to proceed? (yes/no)'):
                terminal.error('aborting OSD provisioning')
                raise SystemExit(0)

        self._execute(plan)
Exemple #13
0
    def zap(self, devices=None):
        devices = devices or self.args.devices

        for device in devices:
            mlogger.info("Zapping: %s", device.abspath)
            if device.is_mapper:
                terminal.error("Refusing to zap the mapper device: {}".format(device))
                raise SystemExit(1)
            if device.is_lvm_member:
                self.zap_lvm_member(device)
            if device.is_lv:
                self.zap_lv(device)
            if device.is_partition:
                self.zap_partition(device)
            if device.is_device:
                self.zap_raw_device(device)

        if self.args.devices:
            terminal.success(
                "Zapping successful for: %s" % ", ".join([str(d) for d in self.args.devices])
            )
        else:
            terminal.success(
                "Zapping successful for OSD: %s" % self.args.osd_id or self.args.osd_fsid
            )
Exemple #14
0
    def main(self):
        sub_command_help = dedent("""
        Activate (BlueStore) OSD on a raw block device(s) based on the
        device label (normally the first block of the device).

            ceph-volume raw activate [/dev/sdb2 ...]

        or

            ceph-volume raw activate --osd-id NUM --osd-uuid UUID

        The device(s) associated with the OSD need to have been prepared
        previously, so that all needed tags and metadata exist.
        """)
        parser = argparse.ArgumentParser(
            prog='ceph-volume raw activate',
            formatter_class=argparse.RawDescriptionHelpFormatter,
            description=sub_command_help,
        )
        parser.add_argument('--device', help='The device for the OSD to start')
        parser.add_argument('--osd-id', help='OSD ID to activate')
        parser.add_argument('--osd-uuid', help='OSD UUID to active')
        parser.add_argument(
            '--no-systemd',
            dest='no_systemd',
            action='store_true',
            help=
            'Skip creating and enabling systemd units and starting OSD services'
        )
        parser.add_argument('--block.db',
                            dest='block_db',
                            help='Path to bluestore block.db block device')
        parser.add_argument('--block.wal',
                            dest='block_wal',
                            help='Path to bluestore block.wal block device')
        parser.add_argument('--no-tmpfs',
                            action='store_true',
                            help='Do not use a tmpfs mount for OSD data dir')

        if not self.argv:
            print(sub_command_help)
            return
        args = parser.parse_args(self.argv)
        self.args = args
        if not args.no_systemd:
            terminal.error('systemd support not yet implemented')
            raise SystemExit(1)

        devs = [args.device]
        if args.block_wal:
            devs.append(args.block_wal)
        if args.block_db:
            devs.append(args.block_db)

        self.activate(devs=devs,
                      start_osd_id=args.osd_id,
                      start_osd_uuid=args.osd_uuid,
                      tmpfs=not args.no_tmpfs,
                      systemd=not self.args.no_systemd)
Exemple #15
0
    def main(self):
        parser = argparse.ArgumentParser(
            prog='ceph-volume activate',
            formatter_class=argparse.RawDescriptionHelpFormatter,
            description=self.help,
        )
        parser.add_argument('--osd-id', help='OSD ID to activate')
        parser.add_argument('--osd-uuid', help='OSD UUID to active')
        parser.add_argument(
            '--no-systemd',
            dest='no_systemd',
            action='store_true',
            help=
            'Skip creating and enabling systemd units and starting OSD services'
        )
        parser.add_argument('--no-tmpfs',
                            action='store_true',
                            help='Do not use a tmpfs mount for OSD data dir')
        self.args = parser.parse_args(self.argv)

        # first try raw
        try:
            RAWActivate([]).activate(
                devs=None,
                start_osd_id=self.args.osd_id,
                start_osd_uuid=self.args.osd_uuid,
                tmpfs=not self.args.no_tmpfs,
                systemd=not self.args.no_systemd,
            )
            return
        except Exception as e:
            terminal.info(f'Failed to activate via raw: {e}')

        # then try lvm
        try:
            LVMActivate([]).activate(
                argparse.Namespace(
                    osd_id=self.args.osd_id,
                    osd_fsid=self.args.osd_uuid,
                    no_tmpfs=self.args.no_tmpfs,
                    no_systemd=self.args.no_systemd,
                ))
            return
        except Exception as e:
            terminal.info(f'Failed to activate via lvm: {e}')

        # then try simple
        try:
            SimpleActivate([]).activate(
                argparse.Namespace(
                    osd_id=self.args.osd_id,
                    osd_fsid=self.args.osd_uuid,
                    no_systemd=self.args.no_systemd,
                ))
            return
        except Exception as e:
            terminal.info(f'Failed to activate via simple: {e}')

        terminal.error('Failed to activate any OSD(s)')
def load(abspath=None):
    parser = Conf()
    try:
        parser.read_path(abspath)
        return parser
    except configparser.ParsingError as error:
        terminal.error('Unable to read configuration file: %s' % abspath)
        terminal.error(str(error))
        logger.exception('Unable to parse INI-style file: %s' % abspath)
Exemple #17
0
def load(abspath=None):
    parser = Conf()
    try:
        parser.read_path(abspath)
        return parser
    except configparser.ParsingError as error:
        terminal.error('Unable to read configuration file: %s' % abspath)
        terminal.error(str(error))
        logger.exception('Unable to parse INI-style file: %s' % abspath)
Exemple #18
0
    def zap(self, args):
        for device in args.devices:
            if disk.is_mapper_device(device):
                terminal.error(
                    "Refusing to zap the mapper device: {}".format(device))
                raise SystemExit(1)
            lv = api.get_lv_from_argument(device)
            if lv:
                # we are zapping a logical volume
                path = lv.lv_path
                self.unmount_lv(lv)
            else:
                # we are zapping a partition
                #TODO: ensure device is a partition
                path = device
                # check to if it is encrypted to close
                partuuid = disk.get_partuuid(device)
                if encryption.status("/dev/mapper/{}".format(partuuid)):
                    dmcrypt_uuid = partuuid
                    self.dmcrypt_close(dmcrypt_uuid)

            mlogger.info("Zapping: %s", path)

            # check if there was a pv created with the
            # name of device
            pvs = api.PVolumes()
            pvs.filter(pv_name=device)
            vgs = set([pv.vg_name for pv in pvs])
            for pv in pvs:
                vg_name = pv.vg_name
                lv = None
                if pv.lv_uuid:
                    lv = api.get_lv(vg_name=vg_name, lv_uuid=pv.lv_uuid)

                if lv:
                    self.unmount_lv(lv)

            if args.destroy:
                for vg_name in vgs:
                    mlogger.info(
                        "Destroying volume group %s because --destroy was given",
                        vg_name)
                    api.remove_vg(vg_name)
                mlogger.info(
                    "Destroying physical volume %s because --destroy was given",
                    device)
                api.remove_pv(device)

            wipefs(path)
            zap_data(path)

            if lv and not pvs:
                # remove all lvm metadata
                lv.clear_tags()

        terminal.success("Zapping successful for: %s" %
                         ", ".join(args.devices))
Exemple #19
0
    def execute(self):
        if not self.args.yes:
            self.strategy.report_pretty(self.filtered_devices)
            terminal.info('The above OSDs would be created if the operation continues')
            if not prompt_bool('do you want to proceed? (yes/no)'):
                devices = ','.join([device.abspath for device in self.args.devices])
                terminal.error('aborting OSD provisioning for %s' % devices)
                raise SystemExit(0)

        self.strategy.execute()
Exemple #20
0
    def execute(self, args):
        strategy = get_strategy(self.get_filtered_devices(args.devices), args)
        if not args.yes:
            strategy.report_pretty()
            terminal.info('The above OSDs would be created if the operation continues')
            if not prompt_bool('do you want to proceed? (yes/no)'):
                terminal.error('aborting OSD provisioning for %s' % ','.join(args.devices))
                raise SystemExit(0)

        strategy.execute()
Exemple #21
0
    def execute(self, args):
        strategy = self._get_strategy(args)
        if not args.yes:
            strategy.report_pretty()
            terminal.info('The above OSDs would be created if the operation continues')
            if not prompt_bool('do you want to proceed? (yes/no)'):
                devices = ','.join([device.abspath for device in args.devices])
                terminal.error('aborting OSD provisioning for %s' % devices)
                raise SystemExit(0)

        strategy.execute()
Exemple #22
0
    def execute(self, args):
        strategy = get_strategy(self.get_filtered_devices(args.devices), args)
        if not args.yes:
            strategy.report_pretty()
            terminal.info(
                'The above OSDs would be created if the operation continues')
            if not prompt_bool('do you want to proceed? (yes/no)'):
                terminal.error('aborting OSD provisioning for %s' %
                               ','.join(args.devices))
                raise SystemExit(0)

        strategy.execute()
Exemple #23
0
    def zap(self, args):
        for device in args.devices:
            if disk.is_mapper_device(device):
                terminal.error("Refusing to zap the mapper device: {}".format(device))
                raise SystemExit(1)
            lv = api.get_lv_from_argument(device)
            if lv:
                # we are zapping a logical volume
                path = lv.lv_path
                self.unmount_lv(lv)
            else:
                # we are zapping a partition
                #TODO: ensure device is a partition
                path = device
                # check to if it is encrypted to close
                partuuid = disk.get_partuuid(device)
                if encryption.status("/dev/mapper/{}".format(partuuid)):
                    dmcrypt_uuid = partuuid
                    self.dmcrypt_close(dmcrypt_uuid)

            mlogger.info("Zapping: %s", path)

            # check if there was a pv created with the
            # name of device
            pvs = api.PVolumes()
            pvs.filter(pv_name=device)
            vgs = set([pv.vg_name for pv in pvs])
            for pv in pvs:
                vg_name = pv.vg_name
                lv = None
                if pv.lv_uuid:
                    lv = api.get_lv(vg_name=vg_name, lv_uuid=pv.lv_uuid)

                if lv:
                    self.unmount_lv(lv)

            if args.destroy:
                for vg_name in vgs:
                    mlogger.info("Destroying volume group %s because --destroy was given", vg_name)
                    api.remove_vg(vg_name)
                if not lv:
                    mlogger.info("Destroying physical volume %s because --destroy was given", device)
                    api.remove_pv(device)

            wipefs(path)
            zap_data(path)

            if lv and not pvs:
                # remove all lvm metadata
                lv.clear_tags()

        terminal.success("Zapping successful for: %s" % ", ".join(args.devices))
Exemple #24
0
    def scan_directory(self, path):
        osd_metadata = {'cluster_name': conf.cluster}
        directory_files = os.listdir(path)
        if 'keyring' not in directory_files:
            raise RuntimeError(
                'OSD files not found, required "keyring" file is not present at: %s' % path
            )
        for _file in os.listdir(path):
            file_path = os.path.join(path, _file)
            if os.path.islink(file_path):
                if os.path.exists(file_path):
                    osd_metadata[_file] = self.scan_device(file_path)
                else:
                    msg = 'broken symlink found %s -> %s' % (file_path, os.path.realpath(file_path))
                    terminal.warning(msg)
                    logger.warning(msg)

            if os.path.isdir(file_path):
                continue

            # the check for binary needs to go before the file, to avoid
            # capturing data from binary files but still be able to capture
            # contents from actual files later
            try:
                if system.is_binary(file_path):
                    logger.info('skipping binary file: %s' % file_path)
                    continue
            except IOError:
                logger.exception('skipping due to IOError on file: %s' % file_path)
                continue
            if os.path.isfile(file_path):
                content = self.get_contents(file_path)
                if 'keyring' in file_path:
                    content = parse_keyring(content)
                try:
                    osd_metadata[_file] = int(content)
                except ValueError:
                    osd_metadata[_file] = content

        # we must scan the paths again because this might be a temporary mount
        path_mounts = system.get_mounts(paths=True)
        device = path_mounts.get(path)

        # it is possible to have more than one device, pick the first one, and
        # warn that it is possible that more than one device is 'data'
        if not device:
            terminal.error('Unable to detect device mounted for path: %s' % path)
            raise RuntimeError('Cannot activate OSD')
        osd_metadata['data'] = self.scan_device(device[0] if len(device) else None)

        return osd_metadata
Exemple #25
0
def load(abspath=None):
    if not os.path.exists(abspath):
        raise exceptions.ConfigurationError(abspath=abspath)

    parser = Conf()
    try:
        ceph_file = open(abspath)
        trimmed_conf = _TrimIndentFile(ceph_file)
        with contextlib.closing(ceph_file):
            parser.readfp(trimmed_conf)
            return parser
    except configparser.ParsingError as error:
        logger.exception('Unable to parse INI-style file: %s' % abspath)
        terminal.error(str(error))
        raise RuntimeError('Unable to read configuration file: %s' % abspath)
Exemple #26
0
def load(abspath=None):
    if not os.path.exists(abspath):
        raise exceptions.ConfigurationError(abspath=abspath)

    parser = Conf()

    try:
        ceph_file = open(abspath)
        trimmed_conf = _TrimIndentFile(ceph_file)
        with contextlib.closing(ceph_file):
            parser.readfp(trimmed_conf)
            return parser
    except configparser.ParsingError as error:
        logger.exception('Unable to parse INI-style file: %s' % abspath)
        terminal.error(str(error))
        raise RuntimeError('Unable to read configuration file: %s' % abspath)
Exemple #27
0
    def main(self):
        sub_command_help = dedent("""
        Activate (BlueStore) OSD on a raw block device based on the 
        device label (normally the first block of the device).

            ceph-volume raw activate --device /dev/sdb
            ceph-volume raw activate --osd-id 1 --osd-fsid f0327efd-c28e-40bb-9199-f2e61e54c12a

        The device(s) associated with the OSD needs to have been prepared
        previously, so that all needed tags and metadata exist.
        """)
        parser = argparse.ArgumentParser(
            prog='ceph-volume raw activate',
            formatter_class=argparse.RawDescriptionHelpFormatter,
            description=sub_command_help,
        )

        parser.add_argument('--device',
                            nargs='+',
                            help='The device(s) for the OSD to start')
        parser.add_argument(
            '--no-systemd',
            dest='no_systemd',
            action='store_true',
            help=
            'Skip creating and enabling systemd units and starting OSD services',
        )
        parser.add_argument('--no-tmpfs',
                            action='store_true',
                            help='Do not use a tmpfs mount for OSD data dir')
        if len(self.argv) == 0:
            print(sub_command_help)
            return
        args = parser.parse_args(self.argv)
        self.args = args
        if not args.no_systemd:
            terminal.error('systemd support not yet implemented')
            raise SystemExit(1)
        self.activate(args.device,
                      tmpfs=not args.no_tmpfs,
                      systemd=not self.args.no_systemd)
Exemple #28
0
def rollback_osd(args, osd_id=None):
    """
    When the process of creating or preparing fails, the OSD needs to be either
    purged (ID fully removed) or destroyed (ID persists). This is important
    because otherwise it would leave the ID around, which can cause confusion
    if relying on the automatic (OSD.N + 1) behavior.

    When the OSD id is specified in the command line explicitly (with
    ``--osd-id``) , the the ID is then preserved with a soft removal (``ceph
    osd destroy``), otherwise it is fully removed with ``purge``.
    """
    if not osd_id:
        # it means that it wasn't generated, so there is nothing to rollback here
        return

    # once here, this is an error condition that needs to be rolled back
    terminal.error('Was unable to complete a new OSD, will rollback changes')
    osd_name = 'osd.%s'
    if args.osd_id is None:
        terminal.error(
            'OSD will be fully purged from the cluster, because the ID was generated'
        )
        # the ID wasn't passed in explicitly, so make sure it is fully removed
        process.run([
            'ceph', 'osd', 'purge', osd_name % osd_id, '--yes-i-really-mean-it'
        ])
    else:
        terminal.error(
            'OSD will be destroyed, keeping the ID because it was provided with --osd-id'
        )
        # the ID was passed explicitly, so allow to re-use by using `destroy`
        process.run([
            'ceph', 'osd', 'destroy', osd_name % args.osd_id,
            '--yes-i-really-mean-it'
        ])
Exemple #29
0
def rollback_osd(args, osd_id=None):
    """
    When the process of creating or preparing fails, the OSD needs to be either
    purged (ID fully removed) or destroyed (ID persists). This is important
    because otherwise it would leave the ID around, which can cause confusion
    if relying on the automatic (OSD.N + 1) behavior.

    When the OSD id is specified in the command line explicitly (with
    ``--osd-id``) , the the ID is then preserved with a soft removal (``ceph
    osd destroy``), otherwise it is fully removed with ``purge``.
    """
    if not osd_id:
        # it means that it wasn't generated, so there is nothing to rollback here
        return

    # once here, this is an error condition that needs to be rolled back
    terminal.error('Was unable to complete a new OSD, will rollback changes')
    osd_name = 'osd.%s'
    if args.osd_id is None:
        terminal.error('OSD will be fully purged from the cluster, because the ID was generated')
        # the ID wasn't passed in explicitly, so make sure it is fully removed
        process.run([
            'ceph', 'osd', 'purge',
            osd_name % osd_id,
            '--yes-i-really-mean-it'])
    else:
        terminal.error('OSD will be destroyed, keeping the ID because it was provided with --osd-id')
        # the ID was passed explicitly, so allow to re-use by using `destroy`
        process.run([
            'ceph', 'osd', 'destroy',
            osd_name % args.osd_id,
            '--yes-i-really-mean-it'])
Exemple #30
0
    def __call__(self, string):
        if not os.path.exists(string):
            error = "Path does not exist: %s" % string
            raise argparse.ArgumentError(None, error)

        arg_is_partition = disk.is_partition(string)
        if arg_is_partition:
            return os.path.abspath(string)
        absolute_path = os.path.abspath(string)
        if not os.path.isdir(absolute_path):
            error = "Argument is not a directory or device which is required to scan"
            raise argparse.ArgumentError(None, error)
        key_files = ['ceph_fsid', 'fsid', 'keyring', 'ready', 'type', 'whoami']
        dir_files = os.listdir(absolute_path)
        for key_file in key_files:
            if key_file not in dir_files:
                terminal.error('All following files must exist in path: %s' %
                               ' '.join(key_files))
                error = "Required file (%s) was not found in OSD dir path: %s" % (
                    key_file, absolute_path)
                raise argparse.ArgumentError(None, error)

        return os.path.abspath(string)
Exemple #31
0
    def __call__(self, string):
        if not os.path.exists(string):
            error = "Path does not exist: %s" % string
            raise argparse.ArgumentError(None, error)

        arg_is_partition = disk.is_partition(string)
        if arg_is_partition:
            return os.path.abspath(string)
        absolute_path = os.path.abspath(string)
        if not os.path.isdir(absolute_path):
            error = "Argument is not a directory or device which is required to scan"
            raise argparse.ArgumentError(None, error)
        key_files = ['ceph_fsid', 'fsid', 'keyring', 'ready', 'type', 'whoami']
        dir_files = os.listdir(absolute_path)
        for key_file in key_files:
            if key_file not in dir_files:
                terminal.error('All following files must exist in path: %s' % ' '.join(key_files))
                error = "Required file (%s) was not found in OSD dir path: %s" % (
                    key_file,
                    absolute_path
                )
                raise argparse.ArgumentError(None, error)

        return os.path.abspath(string)
Exemple #32
0
def prompt_bool(question, _raw_input=None):
    """
    Interface to prompt a boolean (or boolean-like) response from a user.
    Usually a confirmation.
    """
    input_prompt = _raw_input or raw_input
    prompt_format = '--> {question} '.format(question=question)
    response = input_prompt(prompt_format)
    try:
        return str_to_bool(response)
    except ValueError:
        terminal.error('Valid true responses are: y, yes, <Enter>')
        terminal.error('Valid false responses are: n, no')
        terminal.error('That response was invalid, please try again')
        return prompt_bool(question, _raw_input=input_prompt)
Exemple #33
0
    def main(self):
        sub_command_help = dedent("""
        Activate OSDs by mounting devices previously configured to their
        appropriate destination::

            ceph-volume simple activate {ID} {FSID}

        Or using a JSON file directly::

            ceph-volume simple activate --file /etc/ceph/osd/{ID}-{FSID}.json

        The OSD must have been "scanned" previously (see ``ceph-volume simple
        scan``), so that all needed OSD device information and metadata exist.

        A previously scanned OSD would exist like::

            /etc/ceph/osd/{ID}-{FSID}.json


        Environment variables supported:

        CEPH_VOLUME_SIMPLE_JSON_DIR: Directory location for scanned OSD JSON configs
        """)
        parser = argparse.ArgumentParser(
            prog='ceph-volume simple activate',
            formatter_class=argparse.RawDescriptionHelpFormatter,
            description=sub_command_help,
        )
        parser.add_argument(
            'osd_id',
            metavar='ID',
            nargs='?',
            help='The ID of the OSD, usually an integer, like 0')
        parser.add_argument('osd_fsid',
                            metavar='FSID',
                            nargs='?',
                            help='The FSID of the OSD, similar to a SHA1')
        parser.add_argument(
            '--all',
            help='Activate all OSDs with a OSD JSON config',
            action='store_true',
            default=False,
        )
        parser.add_argument('--file',
                            help='The path to a JSON file, from a scanned OSD')
        parser.add_argument(
            '--no-systemd',
            dest='skip_systemd',
            action='store_true',
            help=
            'Skip creating and enabling systemd units and starting OSD services',
        )
        if len(self.argv) == 0:
            print(sub_command_help)
            return
        args = parser.parse_args(self.argv)
        if not args.file and not args.all:
            if not args.osd_id and not args.osd_fsid:
                terminal.error(
                    'ID and FSID are required to find the right OSD to activate'
                )
                terminal.error('from a scanned OSD location in /etc/ceph/osd/')
                raise RuntimeError(
                    'Unable to activate without both ID and FSID')
        # don't allow a CLI flag to specify the JSON dir, because that might
        # implicitly indicate that it would be possible to activate a json file
        # at a non-default location which would not work at boot time if the
        # custom location is not exposed through an ENV var
        self.skip_systemd = args.skip_systemd
        json_dir = os.environ.get('CEPH_VOLUME_SIMPLE_JSON_DIR',
                                  '/etc/ceph/osd/')
        if args.all:
            if args.file or args.osd_id:
                mlogger.warn(
                    '--all was passed, ignoring --file and ID/FSID arguments')
            json_configs = glob.glob('{}/*.json'.format(json_dir))
            for json_config in json_configs:
                mlogger.info(
                    'activating OSD specified in {}'.format(json_config))
                args.json_config = json_config
                try:
                    self.activate(args)
                except RuntimeError as e:
                    terminal.warning(e.message)
        else:
            if args.file:
                json_config = args.file
            else:
                json_config = os.path.join(
                    json_dir, '%s-%s.json' % (args.osd_id, args.osd_fsid))
            if not os.path.exists(json_config):
                raise RuntimeError('Expected JSON config path not found: %s' %
                                   json_config)
            args.json_config = json_config
            self.activate(args)
Exemple #34
0
 def get_ptuuid(self, argument):
     uuid = disk.get_partuuid(argument)
     if not uuid:
         terminal.error('blkid could not detect a PARTUUID for device: %s' % argument)
         raise RuntimeError('unable to use device')
     return uuid
Exemple #35
0
    def zap(self, args):
        device = args.device
        if disk.is_mapper_device(device):
            terminal.error(
                "Refusing to zap the mapper device: {}".format(device))
            raise SystemExit(1)
        lv = api.get_lv_from_argument(device)
        if lv:
            # we are zapping a logical volume
            path = lv.lv_path
        else:
            # we are zapping a partition
            #TODO: ensure device is a partition
            path = device

        mlogger.info("Zapping: %s", path)

        # check if there was a pv created with the
        # name of device
        pv = api.get_pv(pv_name=device)
        if pv:
            vg_name = pv.vg_name
            lv = api.get_lv(vg_name=vg_name)

        dmcrypt = False
        dmcrypt_uuid = None
        if lv:
            if lv.tags.get('ceph.cluster_name') and lv.tags.get('ceph.osd_id'):
                lv_path = "/var/lib/ceph/osd/{}-{}".format(
                    lv.tags['ceph.cluster_name'], lv.tags['ceph.osd_id'])
            else:
                lv_path = lv.path
            dmcrypt_uuid = lv.lv_uuid
            dmcrypt = lv.encrypted
            if system.path_is_mounted(lv_path):
                mlogger.info("Unmounting %s", lv_path)
                system.unmount(lv_path)
        else:
            # we're most likely dealing with a partition here, check to
            # see if it was encrypted
            partuuid = disk.get_partuuid(device)
            if encryption.status("/dev/mapper/{}".format(partuuid)):
                dmcrypt_uuid = partuuid
                dmcrypt = True

        if dmcrypt and dmcrypt_uuid:
            dmcrypt_path = "/dev/mapper/{}".format(dmcrypt_uuid)
            mlogger.info("Closing encrypted path %s", dmcrypt_path)
            encryption.dmcrypt_close(dmcrypt_path)

        if args.destroy and pv:
            logger.info(
                "Found a physical volume created from %s, will destroy all it's vgs and lvs",
                device)
            vg_name = pv.vg_name
            mlogger.info(
                "Destroying volume group %s because --destroy was given",
                vg_name)
            api.remove_vg(vg_name)
            mlogger.info(
                "Destroying physical volume %s because --destroy was given",
                device)
            api.remove_pv(device)
        elif args.destroy and not pv:
            mlogger.info(
                "Skipping --destroy because no associated physical volumes are found for %s",
                device)

        wipefs(path)
        zap_data(path)

        if lv and not pv:
            # remove all lvm metadata
            lv.clear_tags()

        terminal.success("Zapping successful for: %s" % path)
Exemple #36
0
    def zap(self, args):
        device = args.device
        if disk.is_mapper_device(device):
            terminal.error("Refusing to zap the mapper device: {}".format(device))
            raise SystemExit(1)
        lv = api.get_lv_from_argument(device)
        if lv:
            # we are zapping a logical volume
            path = lv.lv_path
        else:
            # we are zapping a partition
            #TODO: ensure device is a partition
            path = device

        mlogger.info("Zapping: %s", path)

        # check if there was a pv created with the
        # name of device
        pv = api.get_pv(pv_name=device)
        if pv:
            vg_name = pv.vg_name
            lv = api.get_lv(vg_name=vg_name)

        dmcrypt = False
        dmcrypt_uuid = None
        if lv:
            if lv.tags.get('ceph.cluster_name') and lv.tags.get('ceph.osd_id'):
                lv_path = "/var/lib/ceph/osd/{}-{}".format(lv.tags['ceph.cluster_name'], lv.tags['ceph.osd_id'])
            else:
                lv_path = lv.path
            dmcrypt_uuid = lv.lv_uuid
            dmcrypt = lv.encrypted
            if system.path_is_mounted(lv_path):
                mlogger.info("Unmounting %s", lv_path)
                system.unmount(lv_path)
        else:
            # we're most likely dealing with a partition here, check to
            # see if it was encrypted
            partuuid = disk.get_partuuid(device)
            if encryption.status("/dev/mapper/{}".format(partuuid)):
                dmcrypt_uuid = partuuid
                dmcrypt = True

        if dmcrypt and dmcrypt_uuid:
            dmcrypt_path = "/dev/mapper/{}".format(dmcrypt_uuid)
            mlogger.info("Closing encrypted path %s", dmcrypt_path)
            encryption.dmcrypt_close(dmcrypt_path)

        if args.destroy and pv:
            logger.info("Found a physical volume created from %s, will destroy all it's vgs and lvs", device)
            vg_name = pv.vg_name
            mlogger.info("Destroying volume group %s because --destroy was given", vg_name)
            api.remove_vg(vg_name)
            mlogger.info("Destroying physical volume %s because --destroy was given", device)
            api.remove_pv(device)
        elif args.destroy and not pv:
            mlogger.info("Skipping --destroy because no associated physical volumes are found for %s", device)

        wipefs(path)
        zap_data(path)

        if lv and not pv:
            # remove all lvm metadata
            lv.clear_tags()

        terminal.success("Zapping successful for: %s" % path)
Exemple #37
0
    def main(self):
        sub_command_help = dedent("""
        Activate OSDs by mounting devices previously configured to their
        appropriate destination::

            ceph-volume simple activate {ID} {FSID}

        Or using a JSON file directly::

            ceph-volume simple activate --file /etc/ceph/osd/{ID}-{FSID}.json

        The OSD must have been "scanned" previously (see ``ceph-volume simple
        scan``), so that all needed OSD device information and metadata exist.

        A previously scanned OSD would exist like::

            /etc/ceph/osd/{ID}-{FSID}.json


        Environment variables supported:

        CEPH_VOLUME_SIMPLE_JSON_DIR: Directory location for scanned OSD JSON configs
        """)
        parser = argparse.ArgumentParser(
            prog='ceph-volume simple activate',
            formatter_class=argparse.RawDescriptionHelpFormatter,
            description=sub_command_help,
        )
        parser.add_argument(
            'osd_id',
            metavar='ID',
            nargs='?',
            help='The ID of the OSD, usually an integer, like 0'
        )
        parser.add_argument(
            'osd_fsid',
            metavar='FSID',
            nargs='?',
            help='The FSID of the OSD, similar to a SHA1'
        )
        parser.add_argument(
            '--all',
            help='Activate all OSDs with a OSD JSON config',
            action='store_true',
            default=False,
        )
        parser.add_argument(
            '--file',
            help='The path to a JSON file, from a scanned OSD'
        )
        parser.add_argument(
            '--no-systemd',
            dest='skip_systemd',
            action='store_true',
            help='Skip creating and enabling systemd units and starting OSD services',
        )
        if len(self.argv) == 0:
            print(sub_command_help)
            return
        args = parser.parse_args(self.argv)
        if not args.file and not args.all:
            if not args.osd_id and not args.osd_fsid:
                terminal.error('ID and FSID are required to find the right OSD to activate')
                terminal.error('from a scanned OSD location in /etc/ceph/osd/')
                raise RuntimeError('Unable to activate without both ID and FSID')
        # don't allow a CLI flag to specify the JSON dir, because that might
        # implicitly indicate that it would be possible to activate a json file
        # at a non-default location which would not work at boot time if the
        # custom location is not exposed through an ENV var
        self.skip_systemd = args.skip_systemd
        json_dir = os.environ.get('CEPH_VOLUME_SIMPLE_JSON_DIR', '/etc/ceph/osd/')
        if args.all:
            if args.file or args.osd_id:
                mlogger.warn('--all was passed, ignoring --file and ID/FSID arguments')
            json_configs = glob.glob('{}/*.json'.format(json_dir))
            for json_config in json_configs:
                mlogger.info('activating OSD specified in {}'.format(json_config))
                args.json_config = json_config
                self.activate(args)
        else:
            if args.file:
                json_config = args.file
            else:
                json_config = os.path.join(json_dir, '%s-%s.json' % (args.osd_id, args.osd_fsid))
            if not os.path.exists(json_config):
                raise RuntimeError('Expected JSON config path not found: %s' % json_config)
            args.json_config = json_config
            self.activate(args)