def run(self, context, args, kwargs, opargs): if not args and not kwargs: raise CommandException(_("Create requires more arguments, see 'help create' for more information")) if len(args) > 1: raise CommandException(_("Wrong syntax for create, see 'help create' for more information")) if len(args) == 1: kwargs[self.parent.primary_key.name] = args.pop(0) if 'name' not in kwargs: raise CommandException(_('Please specify a name for your replication link')) else: name = kwargs.pop('name') master = kwargs.pop('master') slave = kwargs.pop('slave') partners = [master, slave] for ip in context.call_sync('network.config.get_my_ips'): if ip in partners: break else: raise CommandException(_( 'None of provided replication link partners {0}, {1} match any of machine\'s IPs'.format(master, slave) )) datasets = kwargs.pop('datasets', []) if isinstance(datasets, six.string_types): datasets = [datasets] bidirectional = read_value(kwargs.pop('bidirectional', False), ValueType.BOOLEAN) recursive = read_value(kwargs.pop('recursive', False), ValueType.BOOLEAN) replicate_services = read_value(kwargs.pop('replicate_services', False), ValueType.BOOLEAN) if replicate_services and not bidirectional: raise CommandException(_( 'Replication of services is available only when bi-directional replication is selected' )) ns = SingleItemNamespace(None, self.parent) ns.orig_entity = query.wrap(copy.deepcopy(self.parent.skeleton_entity)) ns.entity = query.wrap(copy.deepcopy(self.parent.skeleton_entity)) ns.entity['name'] = name ns.entity['master'] = master ns.entity['partners'] = partners ns.entity['datasets'] = datasets ns.entity['bidirectional'] = bidirectional ns.entity['recursive'] = recursive ns.entity['replicate_services'] = replicate_services context.submit_task( self.parent.create_task, ns.entity, callback=lambda s, t: post_save(ns, s, t) )
def run(self, context, args, kwargs, opargs): if len(args) != 1: raise CommandException( 'Specify whether to on or off identification light') on = read_value(args[0], ValueType.BOOLEAN) context.call_sync('disk.identify', self.parent.entity['id'], on)
def do_set(self, obj, value): if self.condition and not self.condition(obj): raise ValueError( _("Property '{0}' is not settable for this entity".format( self.name))) value = read_value(value, self.type) if self.enum: enum_val = self.enum() if callable(self.enum) else self.enum if (self.type == ValueType.SET and isinstance(value, (list, tuple))): if bool(frozenset(value).symmetric_difference(enum_val)): raise ValueError( "Invalid value for property '{0}'. Should be one of: {1}" .format(self.get_name, ', '.join(enum_val))) elif value not in enum_val: raise ValueError( "Invalid value for property '{0}'. Should be one of: {1}". format(self.get_name, ', '.join(enum_val))) if isinstance(self.set, collections.Callable): self.set(obj, value) return obj.set(self.set, value)
def do_set(self, obj, value, check_entity=None): if not self.can_set(check_entity if check_entity else obj): raise ValueError(_("Property '{0}' is not settable for this entity".format(self.name))) value = read_value(value, self.type) if self.strict and self.enum: enum_val = self.enum(obj) if callable(self.enum) else self.enum if self.type == ValueType.SET: for e in value: if e not in enum_val: raise ValueError("Invalid value for property '{0}'. Should be one of: {1}".format( self.get_name, '; '.join(format_value(i) for i in enum_val)) ) elif value not in enum_val: raise ValueError("Invalid value for property '{0}'. Should be one of: {1}".format( self.get_name, ', '.join(format_value(i) for i in enum_val)) ) if isinstance(self.set, collections.Callable): self.set(obj, value) return q.set(obj, self.set, value)
def do_set(self, obj, value): if self.condition and not self.condition(obj): raise ValueError(_("Property '{0}' is not settable for this entity".format(self.name))) value = read_value(value, self.type) if self.enum: enum_val = self.enum() if callable(self.enum) else self.enum if (self.type == ValueType.SET and isinstance(value, (list, tuple))): if bool(frozenset(value).symmetric_difference(enum_val)): raise ValueError( "Invalid value for property '{0}'. Should be one of: {1}".format( self.get_name, ', '.join(enum_val) ) ) elif value not in enum_val: raise ValueError( "Invalid value for property '{0}'. Should be one of: {1}".format( self.get_name, ', '.join(enum_val)) ) if isinstance(self.set, collections.Callable): self.set(obj, value) return obj.set(self.set, value)
def do_set(self, obj, value, check_entity=None): if not self.can_set(check_entity if check_entity else obj): raise ValueError( _("Property '{0}' is not settable for this entity".format( self.name))) value = read_value(value, self.type) if self.strict and (self.enum or (self.complete and self.context)): enum_val = self.enum() if callable( self.enum) else self.enum or self.complete.choices( self.context, None) if self.type == ValueType.SET: for e in value: if e not in enum_val: raise ValueError( "Invalid value for property '{0}'. Should be one of: {1}" .format( self.get_name, '; '.join(format_value(i) for i in enum_val))) elif value not in enum_val: raise ValueError( "Invalid value for property '{0}'. Should be one of: {1}". format(self.get_name, ', '.join(format_value(i) for i in enum_val))) if isinstance(self.set, collections.Callable): self.set(obj, value) return q.set(obj, self.set, value)
def run(self, context, args, kwargs, opargs): self.context = context self.reboot = read_value(kwargs.get('reboot', self.reboot), tv=ValueType.BOOLEAN) self.task_id = context.submit_task( 'update.updatenow', self.reboot, callback=self.task_callback )
def run(self, context, args, kwargs, opargs): ignore_volumes = read_value(kwargs.pop('ignore_volumes', False), tv=ValueType.BOOLEAN) if args or kwargs: raise CommandException(_( "Wrong syntax for {0}, see 'help {0}' for more information".format( "migration_resume" ) )) tid = context.submit_task('migration.mastermigrate', True, ignore_volumes) return TaskPromise(context, tid)
def run(self, context, args, kwargs, opargs): incremental = read_value(kwargs.pop('incrementasl', 'yes'), ValueType.BOOLEAN) snapshot = read_value(kwargs.pop('snapshot', 'yes'), ValueType.BOOLEAN) dry_run = read_value(kwargs.pop('dry_run', 'no'), ValueType.BOOLEAN) if dry_run: def describe(row): if row['type'] == 'SEND_STREAM': return '{localfs}@{snapshot} -> {remotefs}@{snapshot} ({incr})'.format( incr='incremental' if row.get('incremental') else 'full', **row) if row['type'] == 'DELETE_SNAPSHOTS': return 'reinitialize remote dataset {remotefs}'.format( **row) if row['type'] == 'DELETE_DATASET': return 'delete remote dataset {remotefs} (because it has been deleted locally)'.format( **row) result = context.call_task_sync('backup.sync', self.parent.entity['id'], snapshot, True) if result['state'] != 'FINISHED': raise CommandException('Failed to query backup: {0}'.format( q.get(result, 'error.message'))) return Sequence( Table(result['result'], [ Table.Column('Action type', 'type', ValueType.STRING), Table.Column('Description', describe, ValueType.STRING) ]), "Estimated backup stream size: {0}".format( format_value( sum(a.get('send_size', 0) for a in result['result']), ValueType.SIZE))) else: tid = context.submit_task('backup.sync', self.parent.entity['id'], snapshot) return TaskPromise(context, tid)
def run(self, context, args, kwargs, opargs): if (args or len(kwargs) > 1 or ('reboot' not in kwargs and len(kwargs) == 1)): raise CommandException(_( "Incorrect syntax {0} {1}\n{2}".format(args, kwargs, inspect.getdoc(self)) )) self.context = context self.reboot = read_value(kwargs.get('reboot', self.reboot), tv=ValueType.BOOLEAN) self.task_id = context.submit_task( 'update.updatenow', self.reboot, callback=self.task_callback )
def run(self, context, args, kwargs, opargs): incremental = read_value(kwargs.pop('incrementasl', 'yes'), ValueType.BOOLEAN) snapshot = read_value(kwargs.pop('snapshot', 'yes'), ValueType.BOOLEAN) dry_run = read_value(kwargs.pop('dry_run', 'no'), ValueType.BOOLEAN) if dry_run: def describe(row): if row['type'] == 'SEND_STREAM': return '{localfs}@{snapshot} -> {remotefs}@{snapshot} ({incr})'.format( incr='incremental' if row.get('incremental') else 'full', **row ) if row['type'] == 'DELETE_SNAPSHOTS': return 'reinitialize remote dataset {remotefs}'.format(**row) if row['type'] == 'DELETE_DATASET': return 'delete remote dataset {remotefs} (because it has been deleted locally)'.format(**row) result = context.call_task_sync('backup.sync', self.parent.entity['id'], snapshot, True) if result['state'] != 'FINISHED': raise CommandException('Failed to query backup: {0}'.format(q.get(result, 'error.message'))) return Sequence( Table( result['result'], [ Table.Column('Action type', 'type', ValueType.STRING), Table.Column('Description', describe, ValueType.STRING) ] ), "Estimated backup stream size: {0}".format(format_value( sum(a.get('send_size', 0) for a in result['result']), ValueType.SIZE) ) ) else: tid = context.submit_task('backup.sync', self.parent.entity['id'], snapshot) return TaskPromise(context, tid)
def map_opargs(opargs, context): ns = context.pipe_cwd mapped_opargs = [] for k, o, v in opargs: if ns.has_property(k): mapping = ns.get_mapping(k) mapped_opargs.append((mapping.name, o, read_value(v, mapping.type))) else: raise CommandException( _('Property {0} not found, valid properties are: {1}'.format( k, ','.join([x.name for x in ns.property_mappings if x.list])))) return mapped_opargs
def map_opargs(opargs, context): ns = context.pipe_cwd mapped_opargs = [] for k, o, v in opargs: if ns.has_property(k): mapping = ns.get_mapping(k) mapped_opargs.append((mapping.name, o, read_value(v, mapping.type))) else: raise CommandException(_( 'Property {0} not found, valid properties are: {1}'.format( k, ','.join([x.name for x in ns.property_mappings if x.list]) ) )) return mapped_opargs
def do_append(self, obj, value): if self.type not in (ValueType.SET, ValueType.ARRAY): raise ValueError('Property is not a set or array') value = read_value(value, self.type) oldvalues = q.get(obj, self.set) if oldvalues is not None: newvalues = oldvalues + list(value) else: newvalues = value if isinstance(self.set, collections.Callable): self.set(obj, newvalues) return q.set(obj, self.set, newvalues)
def do_append(self, obj, value): if self.type != ValueType.SET: raise ValueError('Property is not a set') value = read_value(value, self.type) oldvalues = obj.get(self.set) if oldvalues is not None: newvalues = oldvalues + value else: newvalues = value if isinstance(self.set, collections.Callable): self.set(obj, newvalues) return obj.set(self.set, newvalues)
def do_append(self, obj, value): if self.type not in (ValueType.SET, ValueType.ARRAY): raise ValueError('Property is not a set or array') value = read_value(value, self.type) oldvalues = q.get(obj, self.set) if oldvalues is not None: newvalues = oldvalues + value else: newvalues = value if isinstance(self.set, collections.Callable): self.set(obj, newvalues) return q.set(obj, self.set, newvalues)
def do_set(self, obj, value): if self.condition and not self.condition(obj): raise ValueError(_("Property '{0}' is not settable for this entity".format(self.name))) if self.enum: if str(value) not in self.enum: raise ValueError( "Invalid value for property '{0}'. Should be one of: {1}".format( self.get_name, ', '.join(self.enum)) ) value = read_value(value, self.type) if isinstance(self.set, collections.Callable): self.set(obj, value) return obj.set(self.set, value)
def do_remove(self, obj, value): if self.type not in (ValueType.SET, ValueType.ARRAY): raise ValueError('Property is not a set or array') value = read_value(value, self.type) oldvalues = q.get(obj, self.set) newvalues = oldvalues for v in value: if v in newvalues: newvalues.remove(v) else: raise CommandException(_('{0} is not a value in {1}'.format(v, self.set))) if isinstance(self.set, collections.Callable): self.set(obj, newvalues) return q.set(obj, self.set, newvalues)
def do_remove(self, obj, value): if self.type not in (ValueType.SET, ValueType.ARRAY): raise ValueError('Property is not a set or array') value = read_value(value, self.type) oldvalues = q.get(obj, self.set) newvalues = oldvalues for v in value: if v in newvalues: newvalues.remove(v) else: raise CommandException( _('{0} is not a value in {1}'.format(v, self.set))) if isinstance(self.set, collections.Callable): self.set(obj, newvalues) return q.set(obj, self.set, newvalues)
def run(self, context, args, kwargs, opargs): if len(args) != 1: raise CommandException('Specify whether to on or off identification light') on = read_value(args[0], ValueType.BOOLEAN) context.call_sync('disk.identify', self.parent.entity['id'], on)
def run(self, context, args, kwargs, opargs): if not args and not kwargs: raise CommandException(_("create requires more arguments, see 'help create' for more information")) if len(args) > 1: raise CommandException(_("Wrong syntax for create, see 'help create' for more information")) # This magic below make either `create foo` or `create name=foo` work if len(args) == 1: # However, do not allow user to specify name as both implicit and explicit parameter as this suggests a mistake if 'name' in kwargs: raise CommandException(_("Both implicit and explicit 'name' parameters are specified.")) else: kwargs[self.parent.primary_key.name] = args.pop(0) if 'name' not in kwargs: raise CommandException(_('Please specify a name for your pool')) else: name = kwargs.pop('name') volume_type = kwargs.pop('type', 'auto') if volume_type not in VDEV_TYPES: raise CommandException(_( "Invalid volume type {0}. Should be one of: {1}".format(volume_type, VDEV_TYPES) )) if 'disks' not in kwargs: raise CommandException(_("Please specify one or more disks using the disks property")) else: disks = kwargs.pop('disks') if isinstance(disks, six.string_types): disks = [disks] if read_value(kwargs.pop('encryption', False), ValueType.BOOLEAN) is True: encryption = True password = kwargs.get('password', None) else: encryption = False password = None cache_disks = kwargs.pop('cache', []) log_disks = kwargs.pop('log', []) if cache_disks is None: cache_disks = [] if log_disks is None: log_disks = [] if isinstance(cache_disks, six.string_types): cache_disks = [cache_disks] if isinstance(log_disks, six.string_types): log_disks = [log_disks] ns = SingleItemNamespace(None, self.parent) ns.orig_entity = query.wrap(copy.deepcopy(self.parent.skeleton_entity)) ns.entity = query.wrap(copy.deepcopy(self.parent.skeleton_entity)) disks, cache_disks, log_disks = check_disks(context, disks, cache_disks, log_disks) if disks != 'auto': if len(disks) < DISKS_PER_TYPE[volume_type]: raise CommandException(_("Volume type {0} requires at least {1} disks".format( volume_type, DISKS_PER_TYPE[volume_type] ))) if len(disks) > 1 and volume_type == 'disk': raise CommandException(_("Cannot create a volume of type disk with multiple disks")) if volume_type == 'auto': layout = kwargs.pop('layout', 'auto') if layout not in VOLUME_LAYOUTS: raise CommandException(_( "Invalid layout {0}. Should be one of: {1}".format(layout, list(VOLUME_LAYOUTS.keys())) )) else: if disks != 'auto' and len(disks) < DISKS_PER_TYPE[VOLUME_LAYOUTS[layout]]: raise CommandException(_("Volume layout {0} requires at least {1} disks".format(layout, DISKS_PER_TYPE[VOLUME_LAYOUTS[layout]]))) context.submit_task('volume.create_auto', name, 'zfs', layout, disks, cache_disks, log_disks, encryption, password) else: ns.entity['id'] = name ns.entity['topology'] = {} ns.entity['topology']['data'] = [] if volume_type == 'disk': ns.entity['topology']['data'].append( {'type': 'disk', 'path': correct_disk_path(disks[0])}) else: ns.entity['topology']['data'].append({ 'type': volume_type, 'children': [{'type': 'disk', 'path': correct_disk_path(disk)} for disk in disks] }) ns.entity['encrypted'] = encryption if len(cache_disks) > 0: if 'cache' not in ns.entity: ns.entity['topology']['cache'] = [] for disk in cache_disks: ns.entity['topology']['cache'].append({ 'type': 'disk', 'path': correct_disk_path(disk) }) if len(log_disks) > 0: if 'log' not in ns.entity: ns.entity['topology']['log'] = [] if len(log_disks) > 1: ns.entity['topology']['log'].append({ 'type': 'mirror', 'children': [{'type': 'disk', 'path': correct_disk_path(disk)} for disk in log_disks] }) else: ns.entity['topology']['log'].append({ 'type': 'disk', 'path': correct_disk_path(log_disks[0]) }) context.submit_task( self.parent.create_task, ns.entity, password, callback=lambda s, t: post_save(ns, s, t))
def run(self, context, args, kwargs, opargs): if not args and not kwargs: raise CommandException(_("create requires more arguments, see 'help create' for more information")) if len(args) > 1: raise CommandException(_("Wrong syntax for create, see 'help create' for more information")) # This magic below make either `create foo` or `create name=foo` work if len(args) == 1: # However, do not allow user to specify name as both implicit and explicit parameter as this suggests a mistake if 'name' in kwargs: raise CommandException(_("Both implicit and explicit 'name' parameters are specified.")) else: kwargs[self.parent.primary_key.name] = args.pop(0) if 'name' not in kwargs: raise CommandException(_('Please specify a name for your pool')) else: name = kwargs.pop('name') volume_type = kwargs.pop('type', 'auto') if volume_type not in VDEV_TYPES: raise CommandException(_( "Invalid volume type {0}. Should be one of: {1}".format(volume_type, VDEV_TYPES) )) if 'disks' not in kwargs: raise CommandException(_("Please specify one or more disks using the disks property")) else: disks = kwargs.pop('disks') if isinstance(disks, six.string_types): disks = [disks] if read_value(kwargs.pop('encryption', False), ValueType.BOOLEAN) is True: encryption = True password = kwargs.get('password', None) else: encryption = False password = None cache_disks = kwargs.pop('cache', []) log_disks = kwargs.pop('log', []) if cache_disks is None: cache_disks = [] if log_disks is None: log_disks = [] if isinstance(cache_disks, six.string_types): cache_disks = [cache_disks] if isinstance(log_disks, six.string_types): log_disks = [log_disks] ns = SingleItemNamespace(None, self.parent) ns.orig_entity = query.wrap(copy.deepcopy(self.parent.skeleton_entity)) ns.entity = query.wrap(copy.deepcopy(self.parent.skeleton_entity)) disks, cache_disks, log_disks = check_disks(context, disks, cache_disks, log_disks) if len(disks) < DISKS_PER_TYPE[volume_type]: raise CommandException(_("Volume type {0} requires at least {1} disks".format(volume_type, DISKS_PER_TYPE[volume_type]))) if len(disks) > 1 and volume_type == 'disk': raise CommandException(_("Cannot create a volume of type disk with multiple disks")) if volume_type == 'auto': layout = kwargs.pop('layout', 'auto') if layout not in VOLUME_LAYOUTS: raise CommandException(_( "Invalid layout {0}. Should be one of: {1}".format(layout, list(VOLUME_LAYOUTS.keys())) )) else: if len(disks) < DISKS_PER_TYPE[VOLUME_LAYOUTS[layout]]: raise CommandException(_("Volume layout {0} requires at least {1} disks".format(layout, DISKS_PER_TYPE[VOLUME_LAYOUTS[layout]]))) context.submit_task('volume.create_auto', name, 'zfs', layout, disks, cache_disks, log_disks, encryption, password) else: ns.entity['id'] = name ns.entity['topology'] = {} ns.entity['topology']['data'] = [] if volume_type == 'disk': ns.entity['topology']['data'].append( {'type': 'disk', 'path': correct_disk_path(disks[0])}) else: ns.entity['topology']['data'].append({ 'type': volume_type, 'children': [{'type': 'disk', 'path': correct_disk_path(disk)} for disk in disks] }) ns.entity['encrypted'] = encryption if len(cache_disks) > 0: if 'cache' not in ns.entity: ns.entity['topology']['cache'] = [] for disk in cache_disks: ns.entity['topology']['cache'].append({ 'type': 'disk', 'path': correct_disk_path(disk) }) if len(log_disks) > 0: if 'log' not in ns.entity: ns.entity['topology']['log'] = [] if len(log_disks) > 1: ns.entity['topology']['log'].append({ 'type': 'mirror', 'children': [{'type': 'disk', 'path': correct_disk_path(disk)} for disk in log_disks] }) else: ns.entity['topology']['log'].append({ 'type': 'disk', 'path': correct_disk_path(log_disks[0]) }) context.submit_task( self.parent.create_task, ns.entity, password, callback=lambda s, t: post_save(ns, s, t))
def run(self, context, args, kwargs, opargs): if not kwargs.get('name') and not args: raise CommandException('name is a required property') if not kwargs.get('image'): raise CommandException('image is a required property') name = kwargs.get('name') or args[0] image = context.entity_subscribers['docker.image'].query(('names', 'in', kwargs['image']), single=True) if not image: image = q.query(DockerImageNamespace.default_images, ('name', '=', kwargs['image']), single=True) command = kwargs.get('command', []) command = command if isinstance(command, (list, tuple)) else [command] env = ['{0}={1}'.format(k, v) for k, v in kwargs.items() if k.isupper()] presets = image.get('presets') or {} if image else {} ports = presets.get('ports', []) volumes = presets.get('static_volumes', []) for k, v in kwargs.items(): if k.startswith('volume:'): _, container_path = k.split(':', maxsplit=1) volumes.append({ 'container_path': container_path, 'host_path': v, 'readonly': False }) if k.startswith('port:'): _, portspec = k.split(':', maxsplit=1) port, protocol = portspec.split('/', maxsplit=1) protocol = protocol.upper() try: port = int(port) except ValueError: continue if protocol not in ('TCP', 'UDP'): continue mapping = first_or_default(lambda m: m['container_port'] == port and m['protocol'] == protocol, ports) if mapping: mapping['host_port'] = v continue ports.append({ 'container_port': port, 'host_port': v, 'protocol': protocol }) if presets and len(presets.get('volumes', [])) != len(volumes): presets_volumes = set(i['container_path'] for i in presets['volumes']) entered_volumes = set(i['container_path'] for i in volumes) raise CommandException('Required volumes missing: {0}'.format(', '.join(presets_volumes - entered_volumes))) create_args = { 'names': [name], 'image': kwargs['image'], 'host': kwargs.get('host'), 'hostname': kwargs.get('hostname'), 'command': command, 'environment': env, 'volumes': volumes, 'ports': ports, 'autostart': read_value(kwargs.get('autostart', 'no'), ValueType.BOOLEAN), 'expose_ports': read_value( kwargs.get('expose_ports', q.get(presets, 'expose_ports', False)), ValueType.BOOLEAN ), 'interactive': read_value( kwargs.get('interactive', q.get(presets, 'interactive', False)), ValueType.BOOLEAN ) } ns = get_item_stub(context, self.parent, name) tid = context.submit_task(self.parent.create_task, create_args, callback=lambda s, t: post_save(ns, s, t)) return EntityPromise(context, tid, ns)