class DeviceInfoProvider(Provider): @description("Returns list of available device classes") @returns(h.array(str)) def get_classes(self): return ["disk", "network", "cpu", "usb", "serial_port"] @description("Returns list of devices from given class") @accepts(str) @returns( h.any_of(h.ref('DiskDevice'), h.ref('NetworkDevice'), h.ref('CpuDevice'), h.ref('SerialPortDevice'), h.ref('UsbDevice'))) def get_devices(self, dev_class): method = "_get_class_{0}".format(dev_class) if hasattr(self, method): return getattr(self, method)() return None def _get_class_disk(self): result = [] geom.scan() for child in geom.class_by_name('DISK').geoms: result.append({ "path": os.path.join("/dev", child.name), "name": child.name, "mediasize": child.provider.mediasize, "description": child.provider.config['descr'] }) return result def _get_class_multipath(self): result = [] geom.scan() cls = geom.class_by_name('MULTIPATH') if not cls: return [] for child in cls.geoms: result.append({ "path": os.path.join("/dev", child.name), "name": child.name, "mediasize": child.provider.mediasize, "members": [c.provider.name for c in child.consumers] }) return result def _get_class_network(self): result = [] for i in list(netif.list_interfaces().keys()): if i.startswith(tuple(netif.CLONED_PREFIXES)): continue try: desc = get_sysctl( re.sub('(\\w+)([0-9]+)', 'dev.\\1.\\2.%desc', i)) result.append({'name': i, 'description': desc}) except FileNotFoundError: continue return result def _get_class_serial_port(self): result = [] for devices in devinfo.DevInfo().resource_managers['I/O ports'].values( ): for dev in devices: if not dev.name.startswith('uart'): continue result.append({ 'name': dev.name, 'description': dev.desc, 'drivername': dev.drivername, 'location': dev.location, 'start': hex(dev.start), 'size': dev.size }) return result def _get_class_cpu(self): result = [] ncpus = get_sysctl('hw.ncpu') model = get_sysctl('hw.model').strip('\x00') for i in range(0, ncpus): freq = None temp = None with contextlib.suppress(OSError): freq = get_sysctl('dev.cpu.{0}.freq'.format(i)), with contextlib.suppress(OSError): temp = get_sysctl('dev.cpu.{0}.temperature'.format(i)) result.append({'name': model, 'freq': freq, 'temperature': temp}) return result def _get_class_usb(self): result = [] context = usb1.USBContext() for device in context.getDeviceList(): result.append({ 'bus': device.getBusNumber(), 'address': device.getDeviceAddress(), 'manufacturer': device.getManufacturer(), 'product': device.getProduct(), 'vid': device.getVendorID(), 'pid': device.getProductID(), 'class': device.getDeviceClass() }) context.exit() return result
raise TaskException( errno.EAGAIN, 'Got exception while verifying install: {0}'.format(str(e)) ) return { 'checksum': ed['checksum'], 'notfound': ed['notfound'], 'wrongtype': ed['wrongtype'], 'perm': wl, 'error': error_flag, 'warn': warn_flag, } @description("Checks for updates from the update server and downloads them if available") @accepts(h.any_of( bool, None, )) @returns(bool) class CheckFetchUpdateTask(MasterProgressTask): def describe(self): return "Checks for updates from the update server and downloads them if available. " +\ "Returns Ture if updates were found and applied else False" def verify(self, mail=False): block = self.dispatcher.resource_graph.get_resource(update_resource_string) if block is not None and block.busy: raise VerifyException(errno.EBUSY, ( 'An Update Operation (Configuration/ Download/ Applying' 'the Updates) is already in the queue, please retry later' ))
@returns('zfs-pool') def get_config(self): return self.dispatcher.call_sync('zfs.pool.get_boot_pool') @description("Provides information on Boot Environments") class BootEnvironmentsProvider(Provider): @query('boot-environment') def query(self, filter=None, params=None): return bootenvs.query(*(filter or []), **(params or {})) @description( "Creates a clone of the current Boot Environment or of the specified source (optional)" ) @accepts(str, h.any_of(str, None)) class BootEnvironmentCreate(Task): @classmethod def early_describe(cls): return "Cloning Boot Environment" def describe(self, newname, source=None): return TaskDescription("Cloning Boot Environment {source} - new name {name}", name=newname, source=source or '') def verify(self, newname, source=None): return ['system'] def run(self, newname, source=None): if not CreateClone(newname, bename=source): raise TaskException(errno.EIO, 'Cannot create the {0} boot environment'.format(newname))
if not gid: raise RpcException(errno.ENOSPC, 'No free GIDs available') return gid @description("Create an user in the system") @accepts(h.all_of( h.ref('user'), h.required('username'), h.forbidden('builtin'), h.object(properties={'password': {'type': ['string', 'null']}}), h.any_of( h.required('password'), h.required('unixhash', 'nthash'), h.required('password_disabled') ) )) class UserCreateTask(Task): def __init__(self, dispatcher, datastore): super(UserCreateTask, self).__init__(dispatcher, datastore) self.id = None self.created_group = False @classmethod def early_describe(cls): return "Creating user" def describe(self, user): return TaskDescription("Adding user {name}", name=user['username'])
if 'system_time' in props: timestamp = time.mktime(parser.parse(props['system_time'])) bsd.clock_settime(bsd.ClockType.REALTIME, timestamp) if 'timezone' in props: self.configstore.set('system.timezone', props['timezone']) try: self.dispatcher.call_sync('etcd.generation.generate_group', 'localtime') except RpcException as e: raise TaskException( errno.ENXIO, 'Cannot reconfigure system time: {0}'.format(str(e)) ) @accepts(h.any_of(int, None)) @description("Reboots the System") class SystemRebootTask(Task): def __init__(self, dispatcher, datastore): super(SystemRebootTask, self).__init__(dispatcher, datastore) self.finish_event = Event() self.abort_flag = False def describe(self, delay=None): return "System Reboot" def verify(self, delay=None): return ['root'] def reboot_now(self): time.sleep(1)
if 'system_time' in props: timestamp = time.mktime(parser.parse(props['system_time'])) bsd.clock_settime(bsd.ClockType.REALTIME, timestamp) if 'timezone' in props: self.configstore.set('system.timezone', props['timezone']) try: self.dispatcher.call_sync('etcd.generation.generate_group', 'localtime') except RpcException as e: raise TaskException( errno.ENXIO, 'Cannot reconfigure system time: {0}'.format(str(e))) @accepts(h.any_of(int, None)) @description("Reboots the System") class SystemRebootTask(Task): def __init__(self, dispatcher): super(SystemRebootTask, self).__init__(dispatcher) self.finish_event = Event() self.abort_flag = False @classmethod def early_describe(cls): return 'Rebooting system' def describe(self, delay=None): return TaskDescription('Rebooting system with delay {delay} seconds', delay=delay or 0)
node.update(openvpn) self.dispatcher.call_sync('etcd.generation.generate_group', 'openvpn') self.dispatcher.dispatch_event('service.openvpn.changed', { 'operation': 'update', 'ids': None, }) except RpcException as e: raise TaskException(errno.ENXIO, 'Cannot reconfigure OpenVPN: {0}'.format(str(e))) return 'RESTART' @accepts(str, h.any_of(int, None)) @description('Generates Diffie-Hellman parameters and tls-auth key file') class OpenVPNGenerateKeys(Task): ''' To get it working properly you need to set appropriate timeout on dipatcher client. Generation of 2048 bit dh parameters can take a long time. Maybe this task should be ProgressTask type? - need consultation on that ''' @classmethod def early_describe(cls): return 'Generating OpenVPN cryptographic parameters' def describe(self, key_type, key_length): return TaskDescription('Generating {key_type} OpenVPN cryptographic values', key_type=key_type) def verify(self, key_type, key_length): if key_type not in ['dh-parameters', 'tls-auth-key']:
@accepts(str) class ZpoolDestroyTask(ZpoolBaseTask): def run(self, name): try: zfs = libzfs.ZFS() zfs.destroy(name) except libzfs.ZFSException as err: raise TaskException(errno.EFAULT, str(err)) @accepts( str, h.any_of( h.ref('zfs-topology'), None ), h.any_of( h.array(h.ref('zfs-vdev-extension')), None ) ) class ZpoolExtendTask(ZpoolBaseTask): def __init__(self, dispatcher, datastore): super(ZpoolExtendTask, self).__init__(dispatcher, datastore) self.pool = None self.started = False def run(self, pool, new_vdevs, updated_vdevs): try: self.pool = pool
class UpdateProvider(Provider): @accepts() @returns(str) def is_update_available(self): temp_available = update_cache.get('available', timeout=1) if temp_available is not None: return temp_available elif update_cache.is_valid('available'): return temp_available else: raise RpcException( errno.EBUSY, ('Update Availability flag is invalidated, an Update Check' ' might be underway. Try again in some time.')) @accepts() @returns(h.array(str)) def obtain_changelog(self): temp_changelog = update_cache.get('changelog', timeout=1) if temp_changelog is not None: return temp_changelog elif update_cache.is_valid('changelog'): return temp_changelog else: raise RpcException( errno.EBUSY, ('Changelog list is invalidated, an Update Check ' 'might be underway. Try again in some time.')) @accepts() @returns(h.array(h.ref('update-ops'))) def get_update_ops(self): temp_operations = update_cache.get('operations', timeout=1) if temp_operations is not None: return temp_operations elif update_cache.is_valid('operations'): return temp_operations else: raise RpcException( errno.EBUSY, ('Update Operations Dict is invalidated, an Update Check ' 'might be underway. Try again in some time.')) @accepts() @returns(h.ref('update-info')) def update_info(self): if not update_cache.is_valid('available'): raise RpcException( errno.EBUSY, ('Update Availability flag is invalidated, an Update Check' ' might be underway. Try again in some time.')) info_item_list = [ 'available', 'changelog', 'notes', 'notice', 'operations', 'downloaded', 'version', 'installed', 'installed_version' ] return { key: update_cache.get(key, timeout=1) for key in info_item_list } @returns(h.any_of( h.array(h.ref('update-train')), None, )) def trains(self): conf = Configuration.Configuration() conf.LoadTrainsConfig() trains = conf.AvailableTrains() if trains is None: logger.debug( 'The AvailableTrains call returned None. Check your network connection' ) return None seltrain = self.dispatcher.configstore.get('update.train') data = [] for name in list(trains.keys()): if name in conf._trains: train = conf._trains.get(name) else: train = Train.Train(name) data.append({ 'name': train.Name(), 'description': train.Description(), 'sequence': train.LastSequence(), 'current': True if name == seltrain else False, }) return data @accepts() @returns(str) def get_current_train(self): conf = Configuration.Configuration() conf.LoadTrainsConfig() return conf.CurrentTrain() @accepts() @returns(h.ref('update')) def get_config(self): return { 'train': self.dispatcher.configstore.get('update.train'), 'check_auto': self.dispatcher.configstore.get('update.check_auto'), 'update_server': Configuration.Configuration().UpdateServerURL(), } @private @accepts(h.array(str)) def update_cache_invalidate(self, value_list): for item in value_list: update_cache.invalidate(item) @private @accepts(h.object()) def update_cache_putter(self, value_dict): for key, value in value_dict.items(): update_cache.put(key, value) self.dispatcher.dispatch_event('update.update_info.updated', {'operation': 'update'}) @private @accepts(str) @returns(h.any_of(None, str, bool, h.array(str))) def update_cache_getter(self, key): return update_cache.get(key, timeout=1) @private @accepts(str, str, h.any_of(None, h.object(additionalProperties=True))) def update_alert_set(self, update_class, update_version, kwargs=None): # Formulating a query to find any alerts in the current `update_class` # which could be either of ('UpdateAvailable', 'UpdateDownloaded', 'UpdateInstalled') # as well as any alerts for the specified update version string. # The reason I do this is because say an Update is Downloaded (FreeNAS-10-2016051047) # and there is either a previous alert for an older downloaded update OR there is a # previous alert for the same version itself but for it being available instead of being # downloaded already, both of these previous alerts would need to be cancelled and # replaced by 'UpdateDownloaded' for FreeNAS-10-2016051047. if kwargs is None: kwargs = {} existing_update_alerts = self.dispatcher.call_sync( 'alert.query', [('and', [('active', '=', True), ('dismissed', '=', False)]), ('or', [('class', '=', update_class), ('target', '=', update_version)])]) title = UPDATE_ALERT_TITLE_MAP.get(update_class, 'Update Alert') desc = kwargs.get('desc') if desc is None: if update_class == 'UpdateAvailable': desc = 'Latest Update: {0} is available for download'.format( update_version) elif update_class == 'UpdateDownloaded': desc = 'Update containing {0} is downloaded and ready for install'.format( update_version) elif update_class == 'UpdateInstalled': update_installed_bootenv = kwargs.get( 'update_installed_bootenv') if update_installed_bootenv and not update_installed_bootenv[ 0]['on_reboot']: desc = 'Update containing {0} is installed.'.format( update_version) desc += ' Please activate {0} and Reboot to use this updated version'.format( update_installed_bootenv[0]['realname']) else: desc = 'Update containing {0} is installed and activated for next boot'.format( update_version) else: # what state is this? raise RpcException( errno.EINVAL, 'Unknown update alert class: {0}'.format(update_class)) alert_payload = { 'class': update_class, 'title': title, 'target': update_version, 'description': desc } alert_exists = False # Purposely deleting stale alerts later on since if anything (in constructing the payload) # above this fails the exception prevents alert.cancel from being called. for update_alert in existing_update_alerts: if (update_alert['class'] == update_class and update_alert["target"] == update_version and update_alert["description"] == desc): alert_exists = True continue self.dispatcher.call_sync('alert.cancel', update_alert['id']) if not alert_exists: self.dispatcher.call_sync('alert.emit', alert_payload)
node.update(openvpn_updated) self.dispatcher.call_sync('etcd.generation.generate_group', 'openvpn') self.dispatcher.dispatch_event('service.openvpn.changed', { 'operation': 'update', 'ids': None, }) except RpcException as e: raise TaskException(errno.ENXIO, 'Cannot reconfigure OpenVPN: {0}'.format(str(e))) return 'RESTART' @accepts(str, h.any_of(int, None)) @description('Generates Diffie-Hellman parameters and tls-auth key file') class OpenVPNGenerateKeys(Task): """ To get it working properly you need to set appropriate timeout on dipatcher client. Generation of 2048 bit dh parameters can take a long time. Maybe this task should be ProgressTask type? - need consultation on that """ @classmethod def early_describe(cls): return 'Generating OpenVPN cryptographic parameters' def describe(self, key_type, key_length): return TaskDescription('Generating {key_type} OpenVPN cryptographic values', key_type=key_type) def verify(self, key_type, key_length): if key_type not in ['dh-parameters', 'tls-auth-key']:
return True @description("Provides information on Boot Environments") class BootEnvironmentsProvider(Provider): @query('BootEnvironment') @generator def query(self, filter=None, params=None): return bootenvs.query(*(filter or []), stream=True, **(params or {})) @description( "Creates a clone of the current Boot Environment or of the specified source (optional)" ) @accepts(str, h.any_of(str, None)) class BootEnvironmentCreate(Task): @classmethod def early_describe(cls): return "Cloning Boot Environment" def describe(self, newname, source=None): return TaskDescription( "Cloning Boot Environment {source} - new name {name}", name=newname, source=source or '') def verify(self, newname, source=None): return ['system'] def run(self, newname, source=None):
if not gid: raise RpcException(errno.ENOSPC, 'No free GIDs available') return gid @description("Create an user in the system") @accepts(h.all_of( h.ref('User'), h.required('username'), h.forbidden('builtin'), h.object(properties={'password': {'type': ['password', 'null']}}), h.any_of( h.required('password'), h.required('unixhash', 'nthash'), h.required('password_disabled') ) )) class UserCreateTask(Task): def __init__(self, dispatcher): super(UserCreateTask, self).__init__(dispatcher) self.id = None self.created_group = False @classmethod def early_describe(cls): return "Creating user" def describe(self, user): return TaskDescription("Adding user {name}", name=user['username'])