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
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)
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
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)
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)
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)
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)
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
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
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)
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)
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 )
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)
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)
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))
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()
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()
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()
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()
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))
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
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)
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)
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' ])
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'])
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)
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)
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)
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)
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)
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)
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)