class ZpoolProvider(Provider): @description("Lists ZFS pools") @query('zfs-pool') def query(self, filter=None, params=None): zfs = libzfs.ZFS() return wrap(zfs).query(*(filter or []), **(params or {})) @accepts() @returns(h.array(h.ref('zfs-pool'))) def find(self): zfs = libzfs.ZFS() return list(map(lambda p: p.__getstate__(), zfs.find_import())) @accepts() @returns(h.ref('zfs-pool')) def get_boot_pool(self): name = self.configstore.get('system.boot_pool_name') zfs = libzfs.ZFS() return zfs.get(name).__getstate__() @accepts(str) @returns(h.array(str)) def get_disks(self, name): try: zfs = libzfs.ZFS() pool = zfs.get(name) return pool.disks except libzfs.ZFSException, err: raise RpcException(errno.EFAULT, str(err))
class SwapProvider(Provider): @accepts() @returns(h.array(h.ref('swap-mirror'))) @description("Returns information about swap mirrors present in the system" ) def info(self): return get_swap_info(self.dispatcher).values()
class SessionProvider(Provider): @query('session') def query(self, filter=None, params=None): return self.datastore.query('sessions', *(filter or []), **(params or {})) @accepts() @returns(h.array(h.ref('sessions'))) @description("Returns the logged in and active user sessions" + "Does not include the service sessions in this.") def get_live_user_sessions(self): live_user_session_ids = [] for conn in self.dispatcher.ws_server.connections: # The if check for 'uid' below is to seperate the actuall gui/cli # users of the websocket connection from that of system services # like etcd, statd and so on. if hasattr(conn.user, 'uid'): live_user_session_ids.append(conn.session_id) return self.datastore.query('sessions', ('id', 'in', live_user_session_ids), **({})) @description("Returns the loggedin user for the current session") @returns(str) @pass_sender def whoami(self, sender): return sender.user.name
class SystemAdvancedProvider(Provider): @accepts() @returns(h.ref('system-advanced')) def get_config(self): cs = self.configstore return { 'console_cli': cs.get('system.console.cli'), 'console_screensaver': cs.get('system.console.screensaver'), 'serial_console': cs.get('system.serial.console'), 'serial_port': cs.get('system.serial.port'), 'serial_speed': cs.get('system.serial.speed'), 'powerd': cs.get('service.powerd.enable'), 'swapondrive': cs.get('system.swapondrive'), 'autotune': cs.get('system.autotune'), 'debugkernel': cs.get('system.debug.kernel'), 'uploadcrash': cs.get('system.upload_crash'), 'motd': cs.get('system.motd'), 'boot_scrub_internal': cs.get('system.boot_scrub_internal'), 'periodic_notify_user': cs.get('system.periodic.notify_user'), } @description('Returns array of serial port address') @accepts() @returns(h.array(str)) def serial_ports(self): return filter( lambda y: bool(y), system( "/usr/sbin/devinfo -u | grep uart | grep 0x | cut -d- -f 1 | awk '{print $1}'", shell=True)[0].strip('\n').split('\n'))
class SystemInfoProvider(Provider): def __init__(self): self.__version = None @accepts() @returns(h.array(str)) def uname_full(self): return os.uname() @accepts() @returns(str) @description("Return the full version string, e.g. FreeNAS-8.1-r7794-amd64.") def version(self): if self.__version is None: # See #9113 conf = Configuration.Configuration() manifest = conf.SystemManifest() if manifest: self.__version = manifest.Version() else: with open(VERSION_FILE) as fd: self.__version = fd.read().strip() return self.__version @accepts() @returns(float, float, float) def load_avg(self): return os.getloadavg() @accepts() @returns(h.object(properties={ 'cpu_model': str, 'cpu_cores': int, 'memory_size': long, })) def hardware(self): return { 'cpu_model': get_sysctl("hw.model"), 'cpu_cores': get_sysctl("hw.ncpu"), 'memory_size': get_sysctl("hw.physmem") } @accepts() @returns(h.object(properties={ 'system_time': str, 'boot_time': str, 'uptime': str, 'timezone': str, })) def time(self): boot_time = datetime.fromtimestamp(psutil.BOOT_TIME, tz=tz.tzlocal()) return { 'system_time': datetime.now(tz=tz.tzlocal()).isoformat(), 'boot_time': boot_time.isoformat(), 'uptime': (datetime.now(tz=tz.tzlocal()) - boot_time).total_seconds(), 'timezone': time.tzname[time.daylight], }
class SupportProvider(Provider): @accepts(str, str) @returns(h.array(str)) def categories(self, user, password): sw_name = self.dispatcher.call_sync('system.info.version').split( '-')[0].lower() try: r = requests.post( 'https://%s/%s/api/v1.0/categories' % (ADDRESS, sw_name), data=json.dumps({ 'user': user, 'password': password, }), headers={'Content-Type': 'application/json'}, timeout=10, ) data = r.json() except simplejson.JSONDecodeError as e: logger.debug('Failed to decode ticket attachment response: %s', r.text) raise RpcException(errno.EINVAL, 'Failed to decode ticket response') except requests.ConnectionError as e: raise RpcException(errno.ENOTCONN, 'Connection failed: {0}'.format(str(e))) except requests.Timeout as e: raise RpcException(errno.ETIMEDOUT, 'Connection timed out: {0}'.format(str(e))) if 'error' in data: raise RpcException(errno.EINVAL, data['message']) return data
class SystemUIProvider(Provider): @accepts() @returns(h.ref('system-ui')) def get_config(self): protocol = [] if self.configstore.get('service.nginx.http.enable'): protocol.append('HTTP') if self.configstore.get('service.nginx.https.enable'): protocol.append('HTTPS') return { 'webui_procotol': protocol, 'webui_listen': self.configstore.get( 'service.nginx.listen', ), 'webui_http_port': self.configstore.get( 'service.nginx.http.port', ), 'webui_http_redirect_https': self.configstore.get( 'service.nginx.http.redirect_https', ), 'webui_https_certificate': self.configstore.get( 'service.nginx.https.certificate', ), 'webui_https_port': self.configstore.get( 'service.nginx.https.port', ), }
class SystemDatasetProvider(Provider): @private @description("Initializes the .system dataset") @accepts() @returns() def init(self): pool = self.configstore.get('system.dataset.pool') create_system_dataset(self.dispatcher, pool) mount_system_dataset(self.dispatcher, pool, SYSTEM_DIR) @private @description( "Creates directory in .system dataset and returns reference to it") @accepts(str) @returns(str) def request_directory(self, name): path = os.path.join(SYSTEM_DIR, name) if os.path.exists(path): if os.path.isdir(path): return path raise RpcException(errno.EPERM, 'Cannot grant directory {0}'.format(name)) os.mkdir(path) return path @description("Returns current .system dataset parameters") @returns(h.object()) def status(self): return { 'id': self.configstore.get('system.dataset.id'), 'pool': self.configstore.get('system.dataset.pool') }
class NetworkProvider(Provider): @returns(h.ref('network-config')) def get_global_config(self): return ConfigNode('network', self.configstore) @returns(h.array(str)) def get_my_ips(self): ips = [] ifaces = self.dispatcher.call_sync( 'networkd.configuration.query_interfaces') for i, v in ifaces.iteritems(): if 'LOOPBACK' in v['flags']: continue for aliases in v['aliases']: if aliases['address'] and aliases['family'] != 'LINK': ips.append(aliases['address']) return ips
class SystemGeneralProvider(Provider): @accepts() @returns(h.ref('system-general')) def get_config(self): return { 'hostname': self.configstore.get('system.hostname'), 'language': self.configstore.get('system.language'), 'timezone': self.configstore.get('system.timezone'), 'syslog_server': self.configstore.get('system.syslog_server'), 'console_keymap': self.configstore.get('system.console.keymap') } @accepts() @returns(h.array(h.array(str))) def keymaps(self): if not os.path.exists(KEYMAPS_INDEX): return [] rv = [] with open(KEYMAPS_INDEX, 'r') as f: d = f.read() fnd = re.findall(r'^(?P<name>[^#\s]+?)\.kbd:en:(?P<desc>.+)$', d, re.M) for name, desc in fnd: rv.append((name, desc)) return rv @accepts() @returns(h.array(str)) def timezones(self): result = [] for root, _, files in os.walk(ZONEINFO_DIR): for f in files: if f in ( 'zone.tab', ): continue result.append(os.path.join(root, f).replace( ZONEINFO_DIR + '/', '') ) return result
class SharesProvider(Provider): @query('share') def query(self, filter=None, params=None): return self.datastore.query('shares', *(filter or []), **(params or {})) @description("Returns list of supported sharing providers") @returns(h.array(str)) def supported_types(self): result = [] for p in self.dispatcher.plugins.values(): if p.metadata and p.metadata.get('type') == 'sharing': result.append(p.metadata['method']) return result @description("Returns list of clients connected to particular share") def get_connected_clients(self, share_name): share = self.datastore.get_by_id('shares', share_name) if not share: raise RpcException(errno.ENOENT, 'Share not found') return self.dispatcher.call_sync('shares.{0}.get_connected_clients'.format(share['type']), share_name)
def get_connected_clients(self, share_name): share = self.datastore.get_one('shares', ('type', '=', 'nfs'), ('id', '=', share_name)) result = [] f = open('/var/db/mountdtab') for line in f: host, path = line.split() if share['target'] in path: result.append(host) f.close() return result @description("Adds new NFS share") @accepts(h.ref('nfs-share')) class CreateNFSShareTask(Task): def describe(self, share): return "Creating NFS share {0}".format(share['id']) def verify(self, share): return ['service:nfs'] def run(self, share): self.datastore.insert('shares', share) self.dispatcher.call_sync('etcd.generation.generate_group', 'nfs') self.dispatcher.call_sync('services.ensure_started', 'nfs') self.dispatcher.call_sync('services.reload', 'nfs') self.dispatcher.dispatch_event('shares.nfs.changed', { 'operation': 'create', 'ids': [share['id']]
except simplejson.JSONDecodeError as e: logger.debug("Failed to decode ticket attachment response: %s", r.text) raise RpcException(errno.EINVAL, "Failed to decode ticket response") except requests.ConnectionError as e: raise RpcException(errno.ENOTCONN, "Connection failed: {0}".format(str(e))) except requests.Timeout as e: raise RpcException(errno.ETIMEDOUT, "Connection timed out: {0}".format(str(e))) if "error" in data: raise RpcException(errno.EINVAL, data["message"]) return data @description("Submits a new support ticket") @accepts(h.ref("support-ticket")) class SupportSubmitTask(Task): def describe(self, ticket): return "Submitting ticket" def verify(self, ticket): return ["system"] def run(self, ticket): try: version = self.dispatcher.call_sync("system.info.version") sw_name = version.split("-")[0].lower() data = { "title": ticket["subject"], "body": ticket["description"], "version": version.split("-", 1)[-1],
class FilesystemProvider(Provider): @description("Lists contents of given directory") @accepts(str) @returns(h.array(h.ref('directory'))) def list_dir(self, path): result = [] if not os.path.isdir(path): raise RpcException(errno.ENOENT, 'Path {0} is not a directory'.format(path)) for i in os.listdir(path): try: st = os.stat(os.path.join(path, i)) except OSError: continue item = { 'name': i, 'type': get_type(st), 'size': st.st_size, 'modified': st.st_mtime } result.append(item) return result @accepts(str) @returns(h.ref('stat')) def stat(self, path): try: st = os.stat(path) except OSError, err: raise RpcException(err.errno, str(err)) return { 'path': path, 'type': get_type(st), 'atime': st.st_atime, 'mtime': st.st_mtime, 'ctime': st.st_ctime, 'uid': st.st_uid, 'gid': st.st_gid, 'permissions': { 'user': { 'read': st.st_mode & stat.S_IRUSR, 'write': st.st_mode & stat.S_IWUSR, 'execute': st.st_mode & stat.S_IXUSR }, 'group': { 'read': st.st_mode & stat.S_IRGRP, 'write': st.st_mode & stat.S_IWGRP, 'execute': st.st_mode & stat.S_IXGRP }, 'others': { 'read': st.st_mode & stat.S_IROTH, 'write': st.st_mode & stat.S_IWOTH, 'execute': st.st_mode & stat.S_IXOTH }, } }
@accepts(str) def get_connected_clients(self, share_name): share = self.datastore.get_one('shares', ('type', '=', 'nfs'), ('id', '=', share_name)) result = [] f = open('/var/db/mountdtab') for line in f: host, path = line.split() if share['target'] in path: result.append(host) f.close() return result @description("Adds new NFS share") @accepts(h.ref('nfs-share')) class CreateNFSShareTask(Task): def describe(self, share): return "Creating NFS share {0}".format(share['id']) def verify(self, share): return ['service:nfs'] def run(self, share): self.datastore.insert('shares', share) self.dispatcher.call_sync('etcd.generation.generate_group', 'nfs') self.dispatcher.call_sync('services.ensure_started', 'nfs') self.dispatcher.call_sync('services.reload', 'nfs') self.dispatcher.dispatch_event('shares.nfs.changed', { 'operation': 'create', 'ids': [share['id']]
gid = i break 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', 'group'), h.forbidden('builtin', 'logged-in', 'sessions'), h.object({'password': { 'type': 'string' }}), h.any_of(h.required('password'), h.required('unixhash', 'smbhash'), h.required('password_disabled')), )) class UserCreateTask(Task): def describe(self, user): return "Adding user {0}".format(user['username']) def verify(self, user): errors = [] for code, message in check_unixname(user['username']): errors.append(('name', code, message))
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(), } @description("Set the System Updater Cofiguration Settings") @accepts(h.ref('update')) class UpdateConfigureTask(Task): def describe(self): return "System Updater Configure Settings" def verify(self, props): # TODO: Fix this verify's resource allocation as unique task train_to_set = props.get('train') conf = Configuration.Configuration() conf.LoadTrainsConfig() trains = conf.AvailableTrains() or [] if trains: trains = trains.keys() if train_to_set not in trains: raise VerifyException(
conns = filter(lambda c: c.pid == i.pid, psutil.net_connections('inet')) conn = first_or_default(lambda c: c.laddr[1] == 548, conns) if not conn: continue result.append({ 'host': conn.laddr[0], 'share': None, 'user': i.username() }) @description("Adds new AFP share") @accepts(h.ref('afp-share')) class CreateAFPShareTask(Task): def describe(self, share): return "Creating AFP share {0}".format(share['id']) def verify(self, share): return ['service:afp'] def run(self, share): self.datastore.insert('shares', share) self.dispatcher.call_sync('etcd.generation.generate_group', 'afp') self.dispatcher.call_sync('services.ensure_started', 'afp') self.dispatcher.call_sync('services.reload', 'afp') self.dispatcher.dispatch_event('shares.afp.changed', { 'operation': 'create', 'ids': [share['id']]
def get_boot_pool(self): name = self.configstore.get('system.boot_pool_name') zfs = libzfs.ZFS() return zfs.get(name).__getstate__() @accepts(str) @returns(h.array(str)) def get_disks(self, name): try: zfs = libzfs.ZFS() pool = zfs.get(name) return pool.disks except libzfs.ZFSException, err: raise RpcException(errno.EFAULT, str(err)) @returns(h.object()) def get_capabilities(self): return { 'vdev_types': { 'disk': { 'min_devices': 1, 'max_devices': 1 }, 'mirror': { 'min_devices': 2 }, 'raidz1': { 'min_devices': 2 }, 'raidz2': { 'min_devices': 3
@description('Provides access to the alert system') class AlertsProvider(Provider): @query('alert') def query(self, filter=None, params=None): return self.datastore.query('alerts', *(filter or []), **(params or {})) def dismiss(self, id): try: self.datastore.delete('alerts', id) except DatastoreException, e: raise TaskException(errno.EBADMSG, 'Cannot delete alert: {0}'.format(str(e))) @accepts(h.ref('alert')) def emit(self, alert): alertprops = registered_alerts.get(alert['name']) if alertprops is None: raise RpcException( errno.ENOENT, "Alert {0} not registered".format(alert['name'])) # Try to find the first matching namespace emitters = None dot = alert['name'].split('.') for i in xrange(len(dot), 0, -1): namespace = '.'.join(dot[0:i]) afilter = self.datastore.get_one( 'alerts-filters', ('name', '=', namespace), ('severity', '=', alert['severity']),
class DeviceInfoPlugin(Provider): @description("Returns list of available device classes") @returns(h.array(str)) def get_classes(self): return ["disk", "network", "cpu"] @description("Returns list of devices from given class") @accepts(str) @returns( h.any_of(h.ref('disk-device'), h.ref('network-device'), h.ref('cpu-device'))) 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 netif.list_interfaces().keys(): if i.startswith('lo'): continue desc = get_sysctl(re.sub('(\w+)([0-9]+)', 'dev.\\1.\\2.%desc', i)) result.append({'name': i, 'description': desc}) return result def _get_class_cpu(self): pass
@query('alert') def query(self, filter=None, params=None): return self.datastore.query( 'alerts', *(filter or []), **(params or {}) ) def dismiss(self, id): try: self.datastore.delete('alerts', id) except DatastoreException, e: raise TaskException( errno.EBADMSG, 'Cannot delete alert: {0}'.format(str(e)) ) @accepts(h.ref('alert')) def emit(self, alert): alertprops = registered_alerts.get(alert['name']) if alertprops is None: raise RpcException( errno.ENOENT, "Alert {0} not registered".format(alert['name']) ) # Try to find the first matching namespace emitters = None dot = alert['name'].split('.') for i in xrange(len(dot), 0, -1): namespace = '.'.join(dot[0:i]) afilter = self.datastore.get_one( 'alerts-filters', ('name', '=', namespace),
'upgraded': is_upgraded(config), 'scan': config['scan'], 'properties': config['properties'], 'datasets': map(extend_dataset, flatten_datasets(config['root_dataset'])) }) return vol return self.datastore.query('volumes', *(filter or []), callback=extend, **(params or {})) @description("Finds volumes available for import") @accepts() @returns(h.array( h.object(properties={ 'id': str, 'name': str, 'topology': h.ref('zfs-topology'), 'status': str }) )) def find(self): result = [] for pool in self.dispatcher.call_sync('zfs.pool.find'): topology = pool['groups'] for vdev, _ in iterate_vdevs(topology): try: vdev['path'] = self.dispatcher.call_sync( 'disks.partition_to_disk', vdev['path'] ) except RpcException: pass
}) return vol return self.datastore.query('volumes', *(filter or []), callback=extend, **(params or {})) @description("Finds volumes available for import") @accepts() @returns( h.array( h.object( properties={ 'id': str, 'name': str, 'topology': h.ref('zfs-topology'), 'status': str }))) def find(self): result = [] for pool in self.dispatcher.call_sync('zfs.pool.find'): topology = pool['groups'] for vdev, _ in iterate_vdevs(topology): try: vdev['path'] = self.dispatcher.call_sync( 'disks.partition_to_disk', vdev['path']) except RpcException: pass if self.datastore.exists('volumes', ('id', '=', pool['guid'])):
disk = self.datastore.get_by_id('disks', id) acc_level = getattr(AcousticLevel, disk.get('acoustic_level', 'DISABLED')).value powermgmt = disk.get('apm_mode', 0) system('/usr/local/sbin/ataidle', '-P', str(powermgmt), '-A', str(acc_level), disk['path']) if disk.get('standby_mode'): standby_mode = str(disk['standby_mode']) gevent.spawn_later(60, lambda: system( '/usr/local/sbin/ataidle', '-I', standby_mode, disk['path'] )) @accepts(str, str, h.object()) class DiskGPTFormatTask(Task): def describe(self, disk, fstype, params=None): return "Formatting disk {0}".format(os.path.basename(disk)) def verify(self, disk, fstype, params=None): if not get_disk_by_path(disk): raise VerifyException(errno.ENOENT, "Disk {0} not found".format(disk)) if fstype not in ['freebsd-zfs']: raise VerifyException(errno.EINVAL, "Unsupported fstype {0}".format(fstype)) return ['disk:{0}'.format(disk)] def run(self, disk, fstype, params=None): if params is None:
class RouteProvider(Provider): @query('network-route') def query(self, filter=None, params=None): return self.datastore.query('network.routes', *(filter or []), **(params or {})) @description("Provides access to static host entries database") class HostsProvider(Provider): @query('network-host') def query(self, filter=None, params=None): return self.datastore.query('network.hosts', *(filter or []), **(params or {})) @description("Updates global network configuration settings") @accepts(h.ref('network-config')) class NetworkConfigureTask(Task): def verify(self, settings): return ['system'] def run(self, settings): node = ConfigNode('network', self.dispatcher.configstore) node.update(settings) try: self.dispatcher.call_sync('networkd.configuration.configure_network') self.dispatcher.call_sync('etcd.generation.generate_group', 'network') except RpcException, e: raise TaskException(errno.ENXIO, 'Cannot reconfigure interface: {0}'.format(str(e)))
if certificate.get('csr'): certificate['csr_path'] = os.path.join( cert_path, '{0}.csr'.format(certificate['name'])) return certificate return self.datastore.query('crypto.certificates', *(filter or []), callback=extend, **(params or {})) @accepts( h.all_of( h.ref('crypto-certificate'), h.required('signedby', 'name', 'country', 'state', 'city', 'organization', 'email', 'common'), )) class CertificateInternalCreateTask(Task): def verify(self, certificate): errors = [] if self.datastore.exists('crypto.certificates', ('name', '=', certificate['name'])): errors.append(('name', errno.EEXIST, 'Certificate with given name already exists')) if not self.datastore.exists('crypto.certificates', ('id', '=', certificate['signedby'])): errors.append(('signedby', errno.EEXIST,
if not self.datastore.exists('groups', ('id', '=', i)): gid = i break 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', 'group'), h.forbidden('builtin', 'logged-in', 'sessions'), h.object({'password': {'type': 'string'}}), h.any_of( h.required('password'), h.required('unixhash', 'smbhash'), h.required('password_disabled')), )) class UserCreateTask(Task): def describe(self, user): return "Adding user {0}".format(user['username']) def verify(self, user): errors = [] for code, message in check_unixname(user['username']): errors.append(('name', code, message))
def _init(dispatcher, plugin): # Register Schemas plugin.register_schema_definition('update', { 'type': 'object', 'properties': { 'train': {'type': 'string'}, 'check_auto': {'type': 'boolean'}, 'update_server': {'type': 'string', 'readOnly': True}, }, }) plugin.register_schema_definition('update-progress', h.object(properties={ 'operation': h.enum(str, ['DOWNLOADING', 'INSTALLING']), 'details': str, 'indeterminate': bool, 'percent': int, 'reboot': bool, 'pkg_name': str, 'pkg_version': str, 'filename': str, 'filesize': int, 'num_files_done': int, 'num_files_total': int, 'error': bool, 'finished': bool, })) plugin.register_schema_definition('update-ops', { 'type': 'object', 'properties': { 'new_name': {'type': 'string'}, 'previous_version': {'type': 'string'}, 'operation': { 'type': 'string', 'enum': ['delete', 'install', 'upgrade'] }, 'new_version': {'type': 'string'}, 'previous_name': {'type': 'string'}, } }) plugin.register_schema_definition('update-info', { 'type': 'object', 'properties': { 'notes': {'type': 'object'}, 'notice': {'type': 'string'}, 'changelog': {'type': 'string'}, 'operations': {'$ref': 'update-ops'}, } }) plugin.register_schema_definition('update-train', { 'type': 'object', 'properties': { 'name': {'type': 'string'}, 'description': {'type': 'string'}, 'sequence': {'type': 'string'}, 'current': {'type': 'boolean'}, } }) # Register providers plugin.register_provider("update", UpdateProvider) # Register task handlers plugin.register_task_handler("update.configure", UpdateConfigureTask) plugin.register_task_handler("update.check", CheckUpdateTask) plugin.register_task_handler("update.download", DownloadUpdateTask) plugin.register_task_handler("update.manual", UpdateManualTask) plugin.register_task_handler("update.update", UpdateApplyTask) plugin.register_task_handler("update.verify", UpdateVerifyTask) # Register Event Types plugin.register_event_type('update.in_progress', schema=h.ref('update-progress')) plugin.register_event_type('update.changed') # Register reources plugin.register_resource(Resource(update_resource_string), ['system']) # Get the Update Cache (if any) at system boot (and hence in init here) generate_update_cache(dispatcher)
class UpdateProvider(Provider): @accepts() @returns(str) def is_update_available(self): temp_updateAvailable = update_cache.get('updateAvailable', timeout=1) if temp_updateAvailable is not None: return temp_updateAvailable elif update_cache.is_valid('updateAvailable'): return temp_updateAvailable 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_updateOperations = update_cache.get('updateOperations', timeout=1) if temp_updateOperations is not None: return temp_updateOperations elif update_cache.is_valid('updateOperations'): return temp_updateOperations else: raise RpcException( errno.EBUSY, ('Update Operations Dict is invalidated, an Update Check ' 'might be underway. Try again in some time.')) @accepts() @returns(h.any_of( h.ref('update-info'), None, )) def update_info(self): if not update_cache.is_valid('updateAvailable'): raise RpcException( errno.EBUSY, ('Update Availability flag is invalidated, an Update Check' ' might be underway. Try again in some time.')) updateAvailable = update_cache.get('updateAvailable', timeout=1) if not updateAvailable: return None updateOperations = update_cache.get('updateOperations', timeout=1) updateNotes = update_cache.get('updateNotes', timeout=1) updateNotice = update_cache.get('updateNotice', timeout=1) changelog = update_cache.get('changelog', timeout=1) return { 'changelog': changelog, 'notes': updateNotes, 'notice': updateNotice, 'operations': updateOperations, } @returns(h.array(h.ref('update-train'))) def trains(self): conf = Configuration.Configuration() conf.LoadTrainsConfig() trains = conf.AvailableTrains() or {} seltrain = self.dispatcher.configstore.get('update.train') data = [] for name in 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(), }
def configure_disk(self, id): disk = self.datastore.get_by_id('disks', id) acc_level = getattr(AcousticLevel, disk.get('acoustic_level', 'DISABLED')).value powermgmt = disk.get('apm_mode', 0) system('/usr/local/sbin/ataidle', '-P', str(powermgmt), '-A', str(acc_level), disk['path']) if disk.get('standby_mode'): standby_mode = str(disk['standby_mode']) gevent.spawn_later( 60, lambda: system('/usr/local/sbin/ataidle', '-I', standby_mode, disk['path'])) @accepts(str, str, h.object()) class DiskGPTFormatTask(Task): def describe(self, disk, fstype, params=None): return "Formatting disk {0}".format(os.path.basename(disk)) def verify(self, disk, fstype, params=None): if not get_disk_by_path(disk): raise VerifyException(errno.ENOENT, "Disk {0} not found".format(disk)) if fstype not in ['freebsd-zfs']: raise VerifyException(errno.EINVAL, "Unsupported fstype {0}".format(fstype)) return ['disk:{0}'.format(disk)]
'webui_http_port': self.configstore.get( 'service.nginx.http.port', ), 'webui_http_redirect_https': self.configstore.get( 'service.nginx.http.redirect_https', ), 'webui_https_certificate': self.configstore.get( 'service.nginx.https.certificate', ), 'webui_https_port': self.configstore.get( 'service.nginx.https.port', ), } @accepts(h.ref('system-general')) class SystemGeneralConfigureTask(Task): def describe(self): return "System General Settings Configure" def verify(self, props): return ['system'] def run(self, props): if 'hostname' in props: netif.set_hostname(props['hostname']) if 'language' in props: self.configstore.set('system.language', props['language'])
result.append(p.metadata['method']) return result @description("Returns list of clients connected to particular share") def get_connected_clients(self, share_name): share = self.datastore.get_by_id('shares', share_name) if not share: raise RpcException(errno.ENOENT, 'Share not found') return self.dispatcher.call_sync('shares.{0}.get_connected_clients'.format(share['type']), share_name) @description("Creates new share") @accepts(h.all_of( h.ref('share'), h.required('id', 'type', 'target') )) class CreateShareTask(Task): def verify(self, share): return ['system'] def run(self, share): self.join_subtasks(self.run_subtask('share.{0}.create'.format(share['type']), share)) @description("Updates existing share") @accepts(str, h.ref('share')) class UpdateShareTask(Task): def verify(self, name, updated_fields): share = self.datastore.get_by_id('shares', name) if not share:
cert_path, '{0}.key'.format(certificate['name'])) # Load and dump private key to make sure its in desired format # This is code ported from 9.3 and must be reviewed as it may very well be useless certificate['privatekey'] = export_privatekey(certificate['privatekey']) if certificate.get('csr'): certificate['csr_path'] = os.path.join( cert_path, '{0}.csr'.format(certificate['name'])) return certificate return self.datastore.query('crypto.certificates', *(filter or []), callback=extend, **(params or {})) @accepts(h.all_of( h.ref('crypto-certificate'), h.required('signedby', 'name', 'country', 'state', 'city', 'organization', 'email', 'common'), )) class CertificateInternalCreateTask(Task): def verify(self, certificate): errors = [] if self.datastore.exists('crypto.certificates', ('name', '=', certificate['name'])): errors.append(('name', errno.EEXIST, 'Certificate with given name already exists')) if not self.datastore.exists('crypto.certificates', ('id', '=', certificate['signedby'])): errors.append(('signedby', errno.EEXIST, 'Signing certificate does not exists')) if '"' in certificate['name']: errors.append( ('name', errno.EINVAL, 'You cannot issue a certificate with a `"` in its name'))
except SubprocessException as e: # sysctl module compatibility raise OSError(str(e.err)) @description("Provides access to OS tunables") class TunablesProvider(Provider): @query('tunable') def query(self, filter=None, params=None): return self.datastore.query('tunables', *(filter or []), **(params or {})) @description("Adds Tunable") @accepts(h.all_of( h.ref('tunable'), h.required('var', 'value', 'type'), )) class TunableCreateTask(Task): def describe(self, tunable): return "Creating Tunable {0}".format(tunable['var']) def verify(self, tunable): errors = [] if self.datastore.exists('tunables', ('var', '=', tunable['var'])): errors.append( ('var', errno.EEXIST, 'This variable already exists.')) if '"' in tunable['value'] or "'" in tunable['value']: errors.append(('value', errno.EINVAL, 'Quotes are not allowed'))
system('sysctl', '{0}={1}'.format(name, str(value))) except SubprocessException as e: # sysctl module compatibility raise OSError(str(e.err)) @description("Provides access to OS tunables") class TunablesProvider(Provider): @query('tunable') def query(self, filter=None, params=None): return self.datastore.query('tunables', *(filter or []), **(params or {})) @description("Adds Tunable") @accepts(h.all_of( h.ref('tunable'), h.required('var', 'value', 'type'), )) class TunableCreateTask(Task): def describe(self, tunable): return "Creating Tunable {0}".format(tunable['var']) def verify(self, tunable): errors = [] if self.datastore.exists('tunables', ('var', '=', tunable['var'])): errors.append(('var', errno.EEXIST, 'This variable already exists.')) if '"' in tunable['value'] or "'" in tunable['value']: errors.append(('value', errno.EINVAL, 'Quotes are not allowed')) if tunable['type'] in ('LOADER', 'RC') and not VAR_LOADER_RC_RE.match(tunable['var']):
@query('network-route') def query(self, filter=None, params=None): return self.datastore.query('network.routes', *(filter or []), **(params or {})) @description("Provides access to static host entries database") class HostsProvider(Provider): @query('network-host') def query(self, filter=None, params=None): return self.datastore.query('network.hosts', *(filter or []), **(params or {})) @description("Updates global network configuration settings") @accepts(h.ref('network-config')) class NetworkConfigureTask(Task): def verify(self, settings): return ['system'] def run(self, settings): node = ConfigNode('network', self.dispatcher.configstore) node.update(settings) try: self.dispatcher.call_sync( 'networkd.configuration.configure_network') self.dispatcher.call_sync('etcd.generation.generate_group', 'network') except RpcException, e: raise TaskException(
if mail['auth']: server.login(mail['user'], mail['pass']) server.sendmail(mail['from'], to, msg) server.quit() except smtplib.SMTPAuthenticationError as e: raise RpcException(errno.EACCES, 'Authentication error: {0} {1}'.format( e.smtp_code, e.smtp_error)) except Exception as e: logger.error('Failed to send email: {0}'.format(str(e)), exc_info=True) raise RpcException(errno.EFAULT, 'Email send error: {0}'.format(str(e))) except: raise RpcException(errno.EFAULT, 'Unexpected error') @accepts(h.ref('mail')) class MailConfigureTask(Task): def verify(self, mail): return [] def run(self, mail): node = ConfigNode('mail', self.dispatcher.configstore) node.update(mail) try: self.dispatcher.call_sync('etcd.generation.generate_group', 'mail') except RpcException, e: raise TaskException( errno.ENXIO, 'Cannot reconfigure mail: {0}'.format(str(e)) )
from dispatcher.rpc import SchemaHelper as h from lib.system import system, SubprocessException logger = logging.getLogger('NTPPlugin') @description("Provides access to NTP Servers configuration") class NTPServersProvider(Provider): @query('ntp-server') def query(self, filter=None, params=None): return self.datastore.query('ntpservers', *(filter or []), **(params or {})) @description("Adds new NTP Server") @accepts(h.all_of( h.ref('ntp-server'), h.required('address'), ), bool) class NTPServerCreateTask(Task): def describe(self, ntp): return "Creating NTP Server {0}".format(ntp['address']) def verify(self, ntp, force=False): errors = [] try: system('ntpdate', '-q', ntp['address']) except SubprocessException: if not force: errors.append(( 'address',
server.quit() except smtplib.SMTPAuthenticationError as e: raise RpcException( errno.EACCES, 'Authentication error: {0} {1}'.format(e.smtp_code, e.smtp_error)) except Exception as e: logger.error('Failed to send email: {0}'.format(str(e)), exc_info=True) raise RpcException(errno.EFAULT, 'Email send error: {0}'.format(str(e))) except: raise RpcException(errno.EFAULT, 'Unexpected error') @accepts(h.ref('mail')) class MailConfigureTask(Task): def verify(self, mail): return [] def run(self, mail): node = ConfigNode('mail', self.dispatcher.configstore) node.update(mail) try: self.dispatcher.call_sync('etcd.generation.generate_group', 'mail') except RpcException, e: raise TaskException(errno.ENXIO, 'Cannot reconfigure mail: {0}'.format(str(e)))
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(), } @description("Set the System Updater Cofiguration Settings") @accepts(h.ref('update')) class UpdateConfigureTask(Task): def describe(self): return "System Updater Configure Settings" def verify(self, props): # TODO: Fix this verify's resource allocation as unique task train_to_set = props.get('train') conf = Configuration.Configuration() conf.LoadTrainsConfig() trains = conf.AvailableTrains() or [] if trains: trains = trains.keys() if train_to_set not in trains: raise VerifyException( errno.ENOENT, '{0} is not a valid train'.format(train_to_set))
class MailProvider(Provider): @returns(h.ref('mail')) def get_config(self): return ConfigNode('mail', self.configstore) @accepts(h.ref('mail-message'), h.ref('mail')) def send(self, mailmessage, mail=None): if mail is None: mail = ConfigNode('mail', self.configstore).__getstate__() if not mail.get('server') or not mail.get('port'): raise RpcException( errno.EINVAL, 'You must provide an outgoing server and port when sending mail', ) to = mailmessage.get('to') attachments = mailmessage.get('attachments') subject = mailmessage.get('subject') extra_headers = mailmessage.get('extra_headers') if not to: to = self.dispatcher.call_sync('users.query', [('username', '=', 'root')], {'single': True}) if to and to.get('email'): to = [to['email']] if attachments: msg = MIMEMultipart() msg.preamble = mailmessage['message'] map(lambda attachment: msg.attach(attachment), attachments) else: msg = MIMEText(mailmessage['message'], _charset='utf-8') if subject: msg['Subject'] = subject msg['From'] = mailmessage['from'] if mailmessage.get( 'from') else mail['from'] msg['To'] = ', '.join(to) msg['Date'] = formatdate() local_hostname = socket.gethostname() version = self.dispatcher.call_sync('system.info.version').split( '-')[0].lower() msg['Message-ID'] = "<{0}-{1}.{2}@{3}>".format( version, datetime.utcnow().strftime("%Y%m%d.%H%M%S.%f"), base64.urlsafe_b64encode(os.urandom(3)), local_hostname) if not extra_headers: extra_headers = {} for key, val in extra_headers.items(): if key in msg: msg.replace_header(key, val) else: msg[key] = val msg = msg.as_string() try: if mail['encryption'] == 'SSL': klass = smtplib.SMTP_SSL else: klass = smtplib.SMTP server = klass(mail['server'], mail['port'], timeout=300, local_hostname=local_hostname) if mail['encryption'] == 'TLS': server.starttls() if mail['auth']: server.login(mail['user'], mail['pass']) server.sendmail(mail['from'], to, msg) server.quit() except smtplib.SMTPAuthenticationError as e: raise RpcException( errno.EACCES, 'Authentication error: {0} {1}'.format(e.smtp_code, e.smtp_error)) except Exception as e: logger.error('Failed to send email: {0}'.format(str(e)), exc_info=True) raise RpcException(errno.EFAULT, 'Email send error: {0}'.format(str(e))) except: raise RpcException(errno.EFAULT, 'Unexpected error')
def _init(dispatcher, plugin): # Register Schemas plugin.register_schema_definition( 'update', { 'type': 'object', 'properties': { 'train': { 'type': 'string' }, 'check_auto': { 'type': 'boolean' }, 'update_server': { 'type': 'string', 'readOnly': True }, }, }) plugin.register_schema_definition( 'update-progress', h.object( properties={ 'operation': h.enum(str, ['DOWNLOADING', 'INSTALLING']), 'details': str, 'indeterminate': bool, 'percent': int, 'reboot': bool, 'pkg_name': str, 'pkg_version': str, 'filename': str, 'filesize': int, 'num_files_done': int, 'num_files_total': int, 'error': bool, 'finished': bool, })) plugin.register_schema_definition( 'update-ops', { 'type': 'object', 'properties': { 'new_name': { 'type': 'string' }, 'previous_version': { 'type': 'string' }, 'operation': { 'type': 'string', 'enum': ['delete', 'install', 'upgrade'] }, 'new_version': { 'type': 'string' }, 'previous_name': { 'type': 'string' }, } }) plugin.register_schema_definition( 'update-info', { 'type': 'object', 'properties': { 'notes': { 'type': 'object' }, 'notice': { 'type': 'string' }, 'changelog': { 'type': 'string' }, 'operations': { '$ref': 'update-ops' }, } }) plugin.register_schema_definition( 'update-train', { 'type': 'object', 'properties': { 'name': { 'type': 'string' }, 'description': { 'type': 'string' }, 'sequence': { 'type': 'string' }, 'current': { 'type': 'boolean' }, } }) # Register providers plugin.register_provider("update", UpdateProvider) # Register task handlers plugin.register_task_handler("update.configure", UpdateConfigureTask) plugin.register_task_handler("update.check", CheckUpdateTask) plugin.register_task_handler("update.download", DownloadUpdateTask) plugin.register_task_handler("update.manual", UpdateManualTask) plugin.register_task_handler("update.update", UpdateApplyTask) plugin.register_task_handler("update.verify", UpdateVerifyTask) # Register Event Types plugin.register_event_type('update.in_progress', schema=h.ref('update-progress')) plugin.register_event_type('update.changed') # Register reources plugin.register_resource(Resource(update_resource_string), ['system']) # Get the Update Cache (if any) at system boot (and hence in init here) generate_update_cache(dispatcher)
try: if type(rc_scripts) is unicode: system("/usr/sbin/service", rc_scripts, 'onerestart') if type(rc_scripts) is list: for i in rc_scripts: system("/usr/sbin/service", i, 'onerestart') except SubprocessException, e: pass @description("Provides functionality to start, stop, restart or reload service") @accepts( str, h.enum(str, ['start', 'stop', 'restart', 'reload']) ) class ServiceManageTask(Task): def describe(self, name, action): return "{0}ing service {1}".format(action.title(), name) def verify(self, name, action): if not self.datastore.exists('service_definitions', ('name', '=', name)): raise VerifyException(errno.ENOENT, 'Service {0} not found'.format(name)) return ['system'] def run(self, name, action): service = self.datastore.get_one('service_definitions', ('name', '=', name)) rc_scripts = service['rcng'].get('rc-scripts') try: