def verify(self, path, logs=True, cores=False): errors = ValidationException() if path in [None, ''] or path.isspace(): errors.add((0, 'path'), 'The Path is required', code=errno.EINVAL) if errors: raise errors return []
def verify(self, id, updated_fields, force=False): ntp = self.datastore.get_by_id('ntpservers', id) if ntp is None: raise VerifyException(errno.ENOENT, 'NTP Server with given ID does not exist') errors = ValidationException() try: if 'address' in updated_fields: system('ntpdate', '-q', updated_fields['address']) except SubprocessException: if not force: errors.append(( 'address', errno.EINVAL, 'Server could not be reached. Check "Force" to continue regardless.')) minpoll = updated_fields.get('minpoll', ntp.get('minpoll')) maxpoll = updated_fields.get('maxpoll', ntp.get('maxpoll')) if minpoll is not None and maxpoll is not None and not maxpoll > minpoll: errors.append(('maxpoll', errno.EINVAL, 'Max Poll should be higher than Min Poll')) if errors: raise ValidationException(errors) return ['system']
def verify(self, path): errors = ValidationException() if path in [None, ''] or path.isspace(): errors.add((0, 'path'), 'The Path is required', code=errno.EINVAL) if errors: raise errors return ['system']
def verify(self, certificate): if self.datastore.exists('crypto.certificates', ('name', '=', certificate['name'])): raise VerifyException(errno.EEXIST, 'Certificate with given name already exists') if certificate['type'] not in ('CERT_EXISTING', 'CA_EXISTING'): raise VerifyException(errno.EINVAL, 'Invalid certificate type') errors = ValidationException() for i in ('country', 'state', 'city', 'organization', 'email', 'common'): if i in certificate: errors.add((0, i), '{0} is not valid in certificate import'.format(i)) if errors: raise errors if certificate['type'] == 'CERT_EXISTING' and ( 'privatekey' not in certificate or 'passphrase' not in certificate ): raise VerifyException( errno.EINVAL, 'privatekey and passphrase required to import certificate' ) try: if 'privatekey' in certificate: load_privatekey(certificate['privatekey'], certificate.get('passphrase')) except Exception: raise VerifyException(errno.EINVAL, 'Invalid passphrase') return ['system']
def run(self, id, updated_fields): service_def = self.datastore.get_by_id('service_definitions', id) node = ConfigNode('service.{0}'.format(service_def['name']), self.configstore) restart = False reload = False updated_config = updated_fields.get('config') if updated_config is None: return del updated_config['type'] if service_def.get('task'): enable = updated_config.pop('enable', None) try: self.verify_subtask(service_def['task'], updated_config) except RpcException as err: new_err = ValidationException() new_err.propagate(err, [0], [1, 'config']) raise new_err result = self.join_subtasks(self.run_subtask(service_def['task'], updated_config)) restart = result[0] == 'RESTART' reload = result[0] == 'RELOAD' if enable is not None: node['enable'] = enable else: node.update(updated_config) if service_def.get('etcd-group'): self.dispatcher.call_sync('etcd.generation.generate_group', service_def.get('etcd-group')) if 'enable' in updated_config: # Propagate to dependent services for i in service_def.get('dependencies', []): svc_dep = self.datastore.get_by_id('service_definitions', i) self.join_subtasks(self.run_subtask('service.update', i, { 'config': { 'type': 'service-{0}'.format(svc_dep['name']), 'enable': updated_config['enable'] } })) if service_def.get('auto_enable'): # Consult state of services dependent on us for i in self.datastore.query('service_definitions', ('dependencies', 'in', service_def['name'])): enb = self.configstore.get('service.{0}.enable', i['name']) if enb != updated_config['enable']: del updated_config['enable'] break self.dispatcher.call_sync('etcd.generation.generate_group', 'services') self.dispatcher.call_sync('service.apply_state', service_def['name'], restart, reload, timeout=30) self.dispatcher.dispatch_event('service.changed', { 'operation': 'update', 'ids': [service_def['id']] })
def verify(self, rsyncmod): errors = ValidationException() if re.search(r'[/\]]', rsyncmod['name']): errors.add((0, 'name'), 'The name cannot contain slash or a closing square backet.') if errors: raise errors return ['system']
def verify(self, props): errors = ValidationException() # Need to actually check for a valid cert here instead of just checking if not None if ('HTTPS' in props.get('webui_protocol', []) and props.get('webui_https_certificate') is None): errors.add((0, 'webui_https_certificate'), 'HTTPS protocol specified for UI without certificate') if errors: raise errors return ['system']
def verify(self, certificate): errors = ValidationException() if certificate['type'] == 'CERT_INTERNAL': if self.datastore.exists('crypto.certificates', ('name', '=', certificate['name'])): errors.add((0, 'name'), 'Certificate with given name already exists', code=errno.EEXIST) if not self.datastore.exists('crypto.certificates', ('id', '=', certificate['signedby'])): errors.add((0, 'signedby'), 'Signing certificate does not exist', code=errno.EEXIST) if '"' in certificate['name']: errors.add(( (0, 'name'), 'You cannot issue a certificate with a `"` in its name') ) if certificate['type'] in ('CERT_INTERNAL', 'CA_INTERMEDIATE'): if 'signedby' not in certificate or not self.datastore.exists('crypto.certificates', ('id', '=', certificate['signedby'])): errors.add((0, 'signedby'), 'Signing Certificate does not exist', code=errno.EEXIST) if errors: raise errors return ['system']
def verify(self, user): errors = ValidationException() normalize_name(user, 'username') for code, message in check_unixname(user['username']): errors.add((0, 'username'), message, code=code) if self.datastore.exists('users', ('username', '=', user['username'])): raise VerifyException(errno.EEXIST, 'User with given name already exists') if 'groups' in user and len(user['groups']) > 64: errors.add( (0, 'groups'), 'User cannot belong to more than 64 auxiliary groups' ) if user.get('full_name') and ':' in user['full_name']: errors.add((0, 'full_name'), 'The character ":" is not allowed') if 'email' in user: if not EMAIL_REGEX.match(user['email']): errors.add( (0, 'email'), "{0} is an invalid email address".format(user['email']) ) if errors: raise errors return ['system']
def verify(self, ipfs): errors = ValidationException() if 'path' in ipfs: if ipfs['path'] in [None, ''] or ipfs['path'].isspace(): errors.add((0, path), "The provided path: '{0}' is not valid".format(ipfs['path'])) if errors: raise errors return ['system']
def verify(self, props): errors = ValidationException() if 'timezone' in props: timezones = self.dispatcher.call_sync('system.general.timezones') if props['timezone'] not in timezones: errors.add((0, 'timezone'), 'Invalid timezone: {0}'.format(props['timezone'])) if errors: raise errors return ['system']
def verify(self, ipfs): errors = ValidationException() if 'path' in ipfs: if ipfs['path'] in [None, ''] or ipfs['path'].isspace(): errors.add((0, ipfs['path']), "The provided path: '{0}' is not valid".format(ipfs['path'])) if errors: raise errors return ['system']
def verify(self, group): errors = ValidationException() normalize_name(group, 'name') for code, message in check_unixname(group['name']): errors.add((0, 'name'), message, code=code) if errors: raise errors return ['system']
def verify(self, id, updated_fields): errors = ValidationException() normalize_name(updated_fields, 'name') if 'name' in updated_fields: for code, message in check_unixname(updated_fields['name']): errors.add((1, 'name'), message, code=code) if errors: raise errors return ['system']
def verify(self, mail): errors = ValidationException() node = ConfigNode("mail", self.configstore).__getstate__() if mail.get("auth"): if not mail.get("user") and not node["user"]: errors.add((0, "auth"), "Mail authorization requires a username") if not mail.get("pass") and not node["pass"]: errors.add((0, "auth"), "Mail authorization requires a password") if errors: raise errors return []
def verify(self, lldp): errors = ValidationException() node = ConfigNode('service.lldp', self.configstore).__getstate__() node.update(lldp) # Lazy load pycountry due to extra verbose DEBUG logging import pycountry if node['country_code'] and node['country_code'] not in pycountry.countries.indices['alpha2']: errors.add((0, 'country_code'), 'Invalid ISO-3166 alpha 2 code') if errors: raise errors return ['system']
def verify(self, mail): errors = ValidationException() node = ConfigNode('mail', self.configstore).__getstate__() if mail.get('auth'): if not mail.get('user') and not node['user']: errors.add((0, 'auth'), 'Mail authorization requires a username') if not mail.get('pass') and not node['pass']: errors.add((0, 'auth'), 'Mail authorization requires a password') if errors: raise errors return []
def verify(self, afp): errors = ValidationException() dbpath = afp.get('dbpath') if dbpath: if not os.path.exists(dbpath): errors.add((0, 'dbpath'), 'Path does not exist', code=errno.ENOENT) elif not os.path.isdir(dbpath): errors.add((0, 'dbpath'), 'Path is not a directory') if errors: raise errors return ['system']
def verify(self, user): errors = [] for code, message in check_unixname(user['username']): errors.append(('name', code, message)) if self.datastore.exists('users', ('username', '=', user['username'])): raise VerifyException(errno.EEXIST, 'User with given name already exists') if 'id' in user and self.datastore.exists('users', ('id', '=', user['id'])): raise VerifyException(errno.EEXIST, 'User with given UID already exists') if 'groups' in user and len(user['groups']) > 64: errors.append( ('groups', errno.EINVAL, 'User cannot belong to more than 64 auxiliary groups')) if 'full_name' in user and ':' in user['full_name']: errors.append(('full_name', errno.EINVAL, 'The character ":" is not allowed')) if errors: raise ValidationException(errors) return ['system']
def verify(self, tftp): errors = [] if errors: raise ValidationException(errors) return ['system']
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']): errors.append(('var', errno.EINVAL, VAR_SYSCTL_FORMAT)) elif tunable['type'] == 'SYSCTL': if not VAR_SYSCTL_RE.match(tunable['var']): errors.append(('var', errno.EINVAL, VAR_LOADER_RC_FORMAT)) try: sysctl.sysctlnametomib(tunable['var']) except OSError: errors.append( ('var', errno.EINVAL, 'Sysctl variable does not exist')) if errors: raise ValidationException(errors) return ['system']
def verify(self, id, updated_fields, force=False): ntp = self.datastore.get_by_id('ntpservers', id) if ntp is None: raise VerifyException(errno.ENOENT, 'NTP Server with given ID does not exists') errors = [] try: if 'address' in updated_fields: system('ntpdate', '-q', updated_fields['address']) except SubprocessException: if not force: errors.append(( 'address', errno.EINVAL, 'Server could not be reached. Check "Force" to continue regardless.')) minpoll = updated_fields.get('minpoll', ntp.get('minpoll')) maxpoll = updated_fields.get('maxpoll', ntp.get('maxpoll')) if minpoll is not None and maxpoll is not None and not maxpoll > minpoll: errors.append(('maxpoll', errno.EINVAL, 'Max Poll should be higher than Min Poll')) if errors: raise ValidationException(errors) return ['system']
def verify(self, id, updated_fields): errors = ValidationException() if 'var' in updated_fields and self.datastore.exists( 'tunables', ('and', [('var', '=', updated_fields['var']), ('id', '!=', id)]) ): errors.add((1, 'var'), 'This variable already exists.', code=errno.EEXIST) if 'value' in updated_fields: if '"' in updated_fields['value'] or "'" in updated_fields['value']: errors.add((1, 'value'), 'Quotes are not allowed') if errors: raise errors return ['system']
def verify(self, uuid, updated_fields): rsyncmod = self.datastore.get_by_id('rsyncd-module', uuid) if rsyncmod is None: raise VerifyException(errno.ENOENT, 'Rsync module {0} does not exist'.format(uuid)) rsyncmod.update(updated_fields) errors = ValidationException() if re.search(r'[/\]]', rsyncmod['name']): errors.add((1, 'name'), 'The name cannot contain slash or a closing square backet.') if errors: raise errors return ['system']
def verify(self, smb): errors = ValidationException() node = ConfigNode('service.smb', self.configstore).__getstate__() netbiosname = smb.get('netbiosname') if netbiosname is not None: for n in netbiosname: if not validate_netbios_name(n): errors.add((0, 'netbiosname'), 'Invalid name {0}'.format(n)) else: netbiosname = node['netbiosname'] workgroup = smb.get('workgroup') if workgroup is not None: if not validate_netbios_name(workgroup): errors.add((0, 'workgroup'), 'Invalid name') else: workgroup = node['workgroup'] if workgroup.lower() in [i.lower() for i in netbiosname]: errors.add((0, 'netbiosname'), 'NetBIOS and Workgroup must be unique') if errors: raise errors return ['system']
def verify(self, group): errors = ValidationException() normalize_name(group, 'name') for code, message in check_unixname(group['name']): errors.add((0, 'name'), message, code=code) if self.datastore.exists('groups', ('name', '=', group['name'])): errors.add( (0, "name"), 'Group {0} already exists'.format(group['name']), code=errno.EEXIST ) if 'gid' in group and self.datastore.exists('groups', ('gid', '=', group['gid'])): errors.add( (0, "gid"), 'Group with GID {0} already exists'.format(group['gid']), code=errno.EEXIST ) if errors: raise errors return ['system']
def verify(self, id, updated_fields): errors = ValidationException() if 'name' in updated_fields: for code, message in check_unixname(updated_fields['name']): errors.add((1, 'name'), message, code=code) # Check if there is another group with same name being renamed to if self.datastore.exists('groups', ('name', '=', updated_fields['name']), ('id', '!=', id)): errors.add( (1, "name"), 'Group {0} already exists'.format(updated_fields['name']), code=errno.EEXIST ) if errors: raise errors return ['system']
def verify(self, params): errors = ValidationException() path = params.get('path') rmode = params.get('rsync_mode') remote_path = params.get('remote_path') remote_host = params.get('remote_host') remote_module = params.get('remote_module') if path in [None, ''] or path.isspace(): errors.add('path', errno.EINVAL, 'The Path is required') elif not os.path.exists(path): raise VerifyException( errno.ENOENT, "The specified path: '{0}'' does not exist".format( params.get('path'))) if (params.get('remote_host') in ['127.0.0.1', 'localhost'] and rmode == 'SSH' and remote_path is not None and not os.path.exists(remote_path)): raise VerifyException( errno.ENOENT, "The specified path: '{0}'' does not exist".format( remote_path)) if rmode == 'SSH' and (remote_path in [None, ''] or remote_path.isspace()): errors.add('remote_path', errno.EINVAL, 'The Remote Path is required') elif rmode == 'MODULE' and (remote_module in [None, ''] or remote_module.isspace()): errors.add('remote_module', errno.EINVAL, 'The Remote Module is required') if remote_host in [None, ''] or remote_host.isspace(): errors.add('remote_host', errno.EINVAL, 'A Remote Host needs to be specified') if errors: raise errors return []
def verify(self, ntp, force=False): errors = ValidationException() try: system('ntpdate', '-q', ntp['address']) except SubprocessException: if not force: errors.add( (0, 'address'), 'Server could not be reached. Check "Force" to continue regardless.' ) minpoll = ntp.get('minpoll', 6) maxpoll = ntp.get('maxpoll', 10) if not maxpoll > minpoll: errors.add((0, 'maxpoll'), 'Max Poll should be higher than Min Poll') if errors: raise errors return ['system']
def verify(self, tunable): errors = ValidationException() if '"' in tunable['value'] or "'" in tunable['value']: errors.add((1, 'value'), 'Quotes are not allowed') if tunable['type'] in ('LOADER', 'RC') and not VAR_LOADER_RC_RE.match( tunable['var']): errors.add((1, 'var'), VAR_SYSCTL_FORMAT) elif tunable['type'] == 'SYSCTL': if not VAR_SYSCTL_RE.match(tunable['var']): errors.add((1, 'var'), VAR_LOADER_RC_FORMAT) try: sysctl.sysctlnametomib(tunable['var']) except OSError: errors.add((1, 'var'), 'Sysctl variable does not exist') if errors: raise errors return ['system']
def verify(self, params): errors = ValidationException() if self.datastore.get_one('users', ('username', '=', params.get('user'))) is None: raise VerifyException( errno.ENOENT, 'User {0} does not exist'.format(params.get('user')) ) path = params.get('path') rmode = params.get('rsync_mode') remote_path = params.get('remote_path') remote_host = params.get('remote_host') remote_module = params.get('remote_module') if path in [None, ''] or path.isspace(): errors.append(('path', errno.EINVAL, 'The Path is required')) elif not os.path.exists(path): raise VerifyException( errno.ENOENT, "The specified path: '{0}'' does not exist".format(params.get('path')) ) if ( params.get('remote_host') in ['127.0.0.1', 'localhost'] and rmode == 'SSH' and remote_path is not None and not os.path.exists(remote_path) ): raise VerifyException( errno.ENOENT, "The specified path: '{0}'' does not exist".format(remote_path) ) if rmode == 'SSH' and (remote_path in [None, ''] or remote_path.isspace()): errors.append(('remote_path', errno.EINVAL, 'The Remote Path is required')) elif rmode == 'MODULE' and (remote_module in [None, ''] or remote_module.isspace()): errors.append(('remote_module', errno.EINVAL, 'The Remote Module is required')) if remote_host in [None, ''] or remote_host.isspace(): errors.append(('remote_host', errno.EINVAL, 'A Remote Host needs to be specified')) if errors: raise ValidationException(errors) return []
def distribution_thread(self): while True: self.task_queue.peek() self.distribution_lock.acquire() task = self.task_queue.get() try: self.logger.debug("Picked up task %d: %s with args %s", task.id, task.name, task.args) errors = self.verify_schema(self.dispatcher.tasks[task.name], task.args, task.strict_verify) if len(errors) > 0: errors = list(validator.serialize_errors(errors)) self.logger.warning( "Cannot submit task {0}: schema verification failed with errors {1}" .format(task.name, errors)) raise ValidationException(extra=errors) task.instance = task.clazz(self.dispatcher, self.dispatcher.datastore) task.resources = task.instance.verify(*task.args) task.description = task.instance.describe(*task.args) if type(task.resources) is not list: raise ValueError( "verify() returned something else than resource list") except Exception as err: self.logger.warning("Cannot verify task %d: %s", task.id, err) task.set_state(TaskState.FAILED, TaskStatus(0), serialize_error(err)) task.ended.set() self.distribution_lock.release() if not isinstance(err, VerifyException): self.dispatcher.report_error( 'Task {0} verify() method raised invalid exception'. format(err), err) continue task.set_state(TaskState.WAITING) self.task_list.append(task) self.distribution_lock.release() self.schedule_tasks() if task.resources: self.logger.debug("Task %d assigned to resources %s", task.id, ','.join(task.resources))
def verify(self, tunable): errors = ValidationException() if '"' in tunable['value'] or "'" in tunable['value']: errors.add((1, 'value'), 'Quotes are not allowed') if tunable['type'] in ('LOADER', 'RC') and not VAR_LOADER_RC_RE.match(tunable['var']): errors.add((1, 'var'), VAR_SYSCTL_FORMAT) elif tunable['type'] == 'SYSCTL': if not VAR_SYSCTL_RE.match(tunable['var']): errors.add((1, 'var'), VAR_LOADER_RC_FORMAT) try: sysctl.sysctlnametomib(tunable['var']) except OSError: errors.add((1, 'var'), 'Sysctl variable does not exist') if errors: raise errors return ['system']
def verify(self, ups): errors = ValidationException() node = ConfigNode('service.ups', self.configstore).__getstate__() node.update(ups) if node['mode'] == 'MASTER' and not node['driver_port']: errors.add((0, 'driver_port'), 'This field is required') if node['mode'] == 'SLAVE' and not node['remote_host']: errors.add((0, 'remote_host'), 'This field is required') if not re.search(r'^[a-z0-9\.\-_]+$', node['identifier'], re.I): errors.add((0, 'identifier'), 'Use alphanumeric characters, ".", "-" and "_"') for i in ('monitor_user', 'monitor_password'): if re.search(r'[ #]', node[i], re.I): errors.add((0, i), 'Spaces or number signs are not allowed') if errors: raise errors return ['system']
def verify(self, uid, updated_fields): if not self.datastore.exists('users', ('id', '=', uid)): raise VerifyException(errno.ENOENT, 'User does not exists') errors = [] if 'groups' in updated_fields and len(updated_fields['groups']) > 64: errors.append( ('groups', errno.EINVAL, 'User cannot belong to more than 64 auxiliary groups')) if 'full_name' in updated_fields and ':' in updated_fields['full_name']: errors.append(('full_name', errno.EINVAL, 'The character ":" is not allowed')) if errors: raise ValidationException(errors) return ['system']
def verify(self, mail): errors = ValidationException() node = ConfigNode('mail', self.configstore).__getstate__() if mail.get('auth'): if not mail.get('user') and not node['user']: errors.add((0, 'auth'), 'Mail authorization requires a username') if not mail.get('password') and not node['password']: errors.add((0, 'auth'), 'Mail authorization requires a password') if errors: raise errors return []
def verify(self, group): errors = [] for code, message in check_unixname(group['name']): errors.append(('name', code, message)) if self.datastore.exists('groups', ('name', '=', group['name'])): errors.append(("name", errno.EEXIST, 'Group {0} already exists'.format(group['name']))) if 'id' in group and self.datastore.exists('groups', ('id', '=', group['id'])): errors.append( ("id", errno.EEXIST, 'Group with GID {0} already exists'.format(group['id']))) if errors: raise ValidationException(errors) return ['system']
def verify(self, id, updated_fields): # Check if group exists group = self.datastore.get_one('groups', ('id', '=', id)) if group is None: raise VerifyException(errno.ENOENT, 'Group with given ID does not exists') errors = [] for code, message in check_unixname(group['name']): errors.append(('name', code, message)) # Check if there is another group with same name being renamed to if self.datastore.exists('groups', ('name', '=', group['name']), ('name', '!=', id)): errors.append(("name", errno.EEXIST, 'Group {0} already exists'.format(group['name']))) if errors: raise ValidationException(errors) return ['system']
def verify(self, webdav): errors = ValidationException() node = ConfigNode('service.webdav', self.configstore).__getstate__() node.update(webdav) if node['http_port'] == node['https_port']: errors.add((0, 'http_port'), 'HTTP and HTTPS ports cannot be the same') if 'HTTPS' in node['protocol'] and not node['certificate']: errors.add((0, 'certificate'), 'SSL protocol specified without choosing a certificate') if node['certificate']: cert = self.dispatcher.call_sync('crypto.certificate.query', [('id', '=', node['certificate'])]) if not cert: errors.add((0, 'certificate'), 'SSL Certificate not found.') if errors: raise errors return ['system']
def verify(self, ntp, force=False): errors = [] try: system('ntpdate', '-q', ntp['address']) except SubprocessException: if not force: errors.append(( 'address', errno.EINVAL, 'Server could not be reached. Check "Force" to continue regardless.')) minpoll = ntp.get('minpoll', 6) maxpoll = ntp.get('maxpoll', 10) if not maxpoll > minpoll: errors.append(('maxpoll', errno.EINVAL, 'Max Poll should be higher than Min Poll')) if errors: raise ValidationException(errors) return ['system']
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')) if errors: raise ValidationException(errors) return ['system']
def verify(self, id, updated_fields): tunable = self.datastore.get_by_id('tunables', id) if tunable is None: raise VerifyException(errno.ENOENT, 'Tunable with given ID does not exists') errors = [] if 'var' in updated_fields and self.datastore.exists( 'tunables', ('and', [('var', '=', updated_fields['var']), ('id', '!=', id)])): errors.append( ('var', errno.EEXIST, 'This variable already exists.')) if 'value' in updated_fields and '"' in updated_fields[ 'value'] or "'" in updated_fields['value']: errors.append(('value', errno.EINVAL, 'Quotes are not allowed')) if 'type' in updated_fields: if updated_fields['type'] in ('LOADER', 'RC') and not VAR_LOADER_RC_RE.match( tunable['var']): errors.append(('var', errno.EINVAL, VAR_SYSCTL_FORMAT)) elif updated_fields['type'] == 'SYSCTL': if not VAR_SYSCTL_RE.match(tunable['var']): errors.append(('var', errno.EINVAL, VAR_LOADER_RC_FORMAT)) try: sysctl.sysctlnametomib(tunable['var']) except OSError: errors.append(('var', errno.EINVAL, 'Sysctl variable does not exist')) if errors: raise ValidationException(errors) return ['system']
def verify(self, id, updated_fields): errors = ValidationException() if 'var' in updated_fields and self.datastore.exists( 'tunables', ('and', [('var', '=', updated_fields['var']), ('id', '!=', id)])): errors.add((1, 'var'), 'This variable already exists.', code=errno.EEXIST) if 'value' in updated_fields: if '"' in updated_fields['value'] or "'" in updated_fields['value']: errors.add((1, 'value'), 'Quotes are not allowed') if errors: raise errors return ['system']
def run(self, id, updated_fields): service_def = self.datastore.get_by_id('service_definitions', id) if not service_def: raise TaskException(errno.ENOENT, 'Service {0} not found'.format(id)) if 'config' in updated_fields: for x in updated_fields['config']: if x == 'type': continue if not self.configstore.exists('service.{0}.{1}'.format( service_def['name'], x)): raise TaskException( errno.ENOENT, 'Service {0} does not have the following key: {1}'. format(service_def['name'], x)) node = ConfigNode('service.{0}'.format(service_def['name']), self.configstore) restart = False reload = False updated_config = updated_fields.get('config') if updated_config is None: return enable = not node['enable'].value and updated_config['enable'] disable = node['enable'].value and not updated_config['enable'] updated_config.pop('type', None) if service_def.get('task'): try: self.verify_subtask(service_def['task'], updated_config) except RpcException as err: new_err = ValidationException() new_err.propagate(err, [0], [1, 'config']) raise new_err result = self.run_subtask_sync(service_def['task'], updated_config, progress_callback=self.set_progress) restart = result == 'RESTART' reload = result == 'RELOAD' if updated_config.get('enable') is not None: node['enable'] = updated_config['enable'] else: node.update(updated_config) if service_def.get('etcd-group'): self.dispatcher.call_sync('etcd.generation.generate_group', service_def.get('etcd-group')) if 'enable' in updated_config: # Propagate to dependent services for i in service_def.get('dependencies', []): svc_dep = self.datastore.get_by_id('service_definitions', i) self.run_subtask_sync( 'service.update', i, { 'config': { 'type': 'service-{0}'.format(svc_dep['name']), 'enable': updated_config['enable'] } }) if service_def.get('auto_enable'): # Consult state of services dependent on us for i in self.datastore.query( 'service_definitions', ('dependencies', 'in', service_def['name'])): enb = self.configstore.get('service.{0}.enable', i['name']) if enb != updated_config['enable']: del updated_config['enable'] break if 'launchd' in service_def: if enable: load_job(self.dispatcher, service_def) if disable: unload_job(self.dispatcher, service_def) self.dispatcher.call_sync('etcd.generation.generate_group', 'services') self.dispatcher.call_sync('service.apply_state', service_def['name'], restart, reload, timeout=120) self.dispatcher.dispatch_event('service.changed', { 'operation': 'update', 'ids': [service_def['id']] })
def verify(self, ftp): errors = ValidationException() node = ConfigNode('service.ftp', self.configstore).__getstate__() node.update(ftp) pmin = node['passive_ports_min'] if 'passive_ports_min' in ftp: if pmin and (pmin < 1024 or pmin > 65535): errors.add((0, 'passive_ports_min'), 'This value must be between 1024 and 65535, inclusive.') pmax = node['passive_ports_max'] if 'passive_ports_max' in ftp: if pmax and (pmax < 1024 or pmax > 65535): errors.add((0, 'passive_ports_max'), 'This value must be between 1024 and 65535, inclusive.') elif pmax and pmin and pmin >= pmax: errors.add((0, 'passive_ports_max'), 'This value must be higher than minimum passive port.') if node['only_anonymous'] and not node['anonymous_path']: errors.add( ((0, 'anonymous_path'), errno.EINVAL, 'This field is required for anonymous login.') ) if node['tls'] is True and not node['tls_ssl_certificate']: errors.add((0, 'tls_ssl_certificate'), 'TLS specified without certificate.') if node['tls_ssl_certificate']: cert = self.dispatcher.call_sync('crypto.certificate.query', [('id', '=', node['tls_ssl_certificate'])]) if not cert: errors.add((0, 'tls_ssl_certificate'), 'SSL Certificate not found.') if errors: raise errors return ['system']
def verify(self, id, updated_fields): tunable = self.datastore.get_by_id('tunables', id) if tunable is None: raise VerifyException(errno.ENOENT, 'Tunable with given ID does not exist') errors = ValidationException() if 'var' in updated_fields and self.datastore.exists( 'tunables', ('and', [('var', '=', updated_fields['var']), ('id', '!=', id)]) ): errors.add((1, 'var'), 'This variable already exists.', code=errno.EEXIST) if 'value' in updated_fields and '"' in updated_fields['value'] or "'" in updated_fields['value']: errors.add((1, 'value'), 'Quotes are not allowed') if 'type' in updated_fields: if updated_fields['type'] in ('LOADER', 'RC') and not VAR_LOADER_RC_RE.match(tunable['var']): errors.add((1, 'var'), VAR_SYSCTL_FORMAT) elif updated_fields['type'] == 'SYSCTL': if not VAR_SYSCTL_RE.match(tunable['var']): errors.add((1, 'var'), VAR_LOADER_RC_FORMAT) try: sysctl.sysctlnametomib(tunable['var']) except OSError: errors.add((1, 'var'), 'Sysctl variable does not exist') if errors: raise errors return ['system']
def verify(self, id, updated_fields): errors = ValidationException() normalize_name(updated_fields, 'username') if updated_fields.get('full_name') and ':' in updated_fields['full_name']: errors.add((1, 'full_name'), 'The character ":" is not allowed') if 'groups' in updated_fields and len(updated_fields['groups']) > 64: errors.add((1, 'groups'), 'User cannot belong to more than 64 auxiliary groups') if 'username' in updated_fields: for code, message in check_unixname(updated_fields['username']): errors.add((1, 'username'), message, code=code) if updated_fields.get('email'): if not EMAIL_REGEX.match(updated_fields['email']): errors.add( (1, 'email'), "{0} is an invalid email address".format(updated_fields['email']) ) if 'home' in updated_fields and updated_fields['home'] not in (None, '/nonexistent'): volumes_root = self.dispatcher.call_sync('volume.get_volumes_root') updated_fields['home'] = os.path.normpath(updated_fields['home']) if not updated_fields['home'].startswith(volumes_root) or updated_fields['home'] == volumes_root: errors.add( (1, 'home directory'), "Invalid mountpoint specified for home directory: {0}.\n".format(updated_fields['home']) + "Provide a path within zfs pool or dataset mounted under {0}".format(volumes_root) ) if errors: raise errors return ['system']
def run(self, id, updated_fields): service_def = self.datastore.get_by_id('service_definitions', id) if not service_def: raise TaskException( errno.ENOENT, 'Service {0} not found'.format(id) ) if 'config' in updated_fields: for x in updated_fields['config']: if x == 'type': continue if not self.configstore.exists('service.{0}.{1}'.format(service_def['name'], x)): raise TaskException( errno.ENOENT, 'Service {0} does not have the following key: {1}'.format( service_def['name'], x)) node = ConfigNode('service.{0}'.format(service_def['name']), self.configstore) restart = False reload = False updated_config = updated_fields.get('config') if updated_config is None: return enable = not node['enable'].value and updated_config['enable'] disable = node['enable'].value and not updated_config['enable'] updated_config.pop('type', None) if service_def.get('task'): try: self.verify_subtask(service_def['task'], updated_config) except RpcException as err: new_err = ValidationException() new_err.propagate(err, [0], [1, 'config']) raise new_err result = self.run_subtask_sync(service_def['task'], updated_config) restart = result == 'RESTART' reload = result == 'RELOAD' if updated_config.get('enable') is not None: node['enable'] = updated_config['enable'] else: node.update(updated_config) if service_def.get('etcd-group'): self.dispatcher.call_sync('etcd.generation.generate_group', service_def.get('etcd-group')) if 'enable' in updated_config: # Propagate to dependent services for i in service_def.get('dependencies', []): svc_dep = self.datastore.get_by_id('service_definitions', i) self.run_subtask_sync('service.update', i, { 'config': { 'type': 'service-{0}'.format(svc_dep['name']), 'enable': updated_config['enable'] } }) if service_def.get('auto_enable'): # Consult state of services dependent on us for i in self.datastore.query('service_definitions', ('dependencies', 'in', service_def['name'])): enb = self.configstore.get('service.{0}.enable', i['name']) if enb != updated_config['enable']: del updated_config['enable'] break if 'launchd' in service_def: if enable: load_job(self.dispatcher, service_def) if disable: unload_job(self.dispatcher, service_def) self.dispatcher.call_sync('etcd.generation.generate_group', 'services') self.dispatcher.call_sync('service.apply_state', service_def['name'], restart, reload, timeout=120) self.dispatcher.dispatch_event('service.changed', { 'operation': 'update', 'ids': [service_def['id']] })
def verify(self, ftp): errors = ValidationException() node = ConfigNode('service.ftp', self.configstore).__getstate__() node.update(ftp) pmin = node['passive_ports_min'] if 'passive_ports_min' in ftp: if pmin and (pmin < 1024 or pmin > 65535): errors.add( (0, 'passive_ports_min'), 'This value must be between 1024 and 65535, inclusive.') pmax = node['passive_ports_max'] if 'passive_ports_max' in ftp: if pmax and (pmax < 1024 or pmax > 65535): errors.add( (0, 'passive_ports_max'), 'This value must be between 1024 and 65535, inclusive.') elif pmax and pmin and pmin >= pmax: errors.add( (0, 'passive_ports_max'), 'This value must be higher than minimum passive port.') if node['only_anonymous'] and not node['anonymous_path']: errors.add((0, 'anonymous_path'), 'This field is required for anonymous login.') if node['tls'] is True and not node['tls_ssl_certificate']: errors.add((0, 'tls_ssl_certificate'), 'TLS specified without certificate.') if node['tls_ssl_certificate']: cert = self.dispatcher.call_sync( 'crypto.certificate.query', [('id', '=', node['tls_ssl_certificate'])]) if not cert: errors.add((0, 'tls_ssl_certificate'), 'SSL Certificate not found.') if node['only_anonymous'] and node['only_local']: errors.add(( 0, 'only_anonymous' ), 'Anonymous only and local only types of authentication cannot be enabled together.' ) if errors: raise errors return ['system']
def verify(self, rsyncd): if rsyncd.get('port') and rsyncd['port'] > 65535: raise ValidationException( errno.EINVAL, 'Port number cannot be greater than 65535') return ['system']