def validate(self, value): if value is None: return verrors = ValidationErrors() s = set() for i, v in enumerate(value): if self.unique: if isinstance(v, dict): v = tuple(sorted(list(v.items()))) if v in s: verrors.add(f"{self.name}.{i}", "This value is not unique.") s.add(v) attr_verrors = ValidationErrors() for attr in self.items: try: attr.validate(v) except ValidationErrors as e: attr_verrors.add_child(f"{self.name}.{i}", e) else: break else: verrors.extend(attr_verrors) if verrors: raise verrors super().validate(value)
def validate(self, value): verrors = ValidationErrors() if value: try: if self.cidr: self.factory(value, strict=self.cidr_strict) else: has_zone_index = False if self.allow_zone_index and "%" in value: has_zone_index = True value = value[:value.rindex("%")] addr = self.factory(value) if has_zone_index and not isinstance( addr, ipaddress.IPv6Address): raise ValueError( "Zone index is allowed only for IPv6 addresses") except ValueError as e: verrors.add(self.name, str(e), errno.EINVAL) if verrors: raise verrors return super().validate(value)
def validate(self, value): if value is None: return verrors = ValidationErrors() if value: try: if self.network: self.factory(value, strict=self.network_strict) else: if self.cidr and '/' not in value: raise ValueError( 'Specified address should be in CIDR notation, e.g. 192.168.0.2/24' ) has_zone_index = False if self.allow_zone_index and "%" in value: has_zone_index = True value = value[:value.rindex("%")] addr = self.factory(value) if has_zone_index and not isinstance( addr, ipaddress.IPv6Address): raise ValueError( "Zone index is allowed only for IPv6 addresses") except ValueError as e: verrors.add(self.name, str(e), errno.EINVAL) if verrors: raise verrors return super().validate(value)
def do_update(self, jail, options): """Sets a jail property.""" plugin = options.pop("plugin") _, _, iocage = self.check_jail_existence(jail) name = options.pop("name", None) verrors = ValidationErrors() jail = self.query([['id', '=', jail]], {'get': True}) verrors = self.common_validation(verrors, options, True, jail) if name is not None and plugin: verrors.add('options.plugin', 'Cannot be true while trying to rename') if verrors: raise verrors for prop, val in options.items(): p = f"{prop}={val}" try: iocage.set(p, plugin) except RuntimeError as err: raise CallError(err) if name: iocage.rename(name) return True
def validate(self, value): if value is None: return verrors = ValidationErrors() if value: try: if self.network: self.factory(value, strict=self.network_strict) else: if self.cidr and '/' not in value: raise ValueError( 'Specified address should be in CIDR notation, e.g. 192.168.0.2/24' ) has_zone_index = False if self.allow_zone_index and "%" in value: has_zone_index = True value = value[:value.rindex("%")] addr = self.factory(value) if has_zone_index and not isinstance(addr, ipaddress.IPv6Address): raise ValueError("Zone index is allowed only for IPv6 addresses") except ValueError as e: verrors.add(self.name, str(e), errno.EINVAL) if verrors: raise verrors return super().validate(value)
def construct_schema( item_version_details: dict, new_values: dict, update: bool, old_values: Union[dict, object] = NOT_PROVIDED ) -> dict: schema_name = f'chart_release_{"update" if update else "create"}' attrs = list(itertools.chain.from_iterable( get_schema(q, update, old_values) for q in item_version_details['schema']['questions'] )) dict_obj = update_conditional_defaults( Dict(schema_name, *attrs, update=update, additional_attrs=True), { 'schema': {'attrs': item_version_details['schema']['questions']} } ) verrors = ValidationErrors() verrors.add_child('values', validate_schema( attrs, new_values, True, dict_kwargs={ 'conditional_defaults': dict_obj.conditional_defaults, 'update': update, } )) return { 'verrors': verrors, 'new_values': new_values, 'dict_obj': dict_obj, 'schema_name': schema_name, }
def upgrade(self, job, jail, options): """Upgrades specified jail to specified RELEASE.""" verrors = ValidationErrors() release = options.get('release', None) plugin = options['plugin'] if release is None and not plugin: verrors.add( 'options.release', 'Must not be None if options.plugin is False.' ) raise verrors job.set_progress(0, f'Upgrading {jail}') msg_queue = deque(maxlen=10) def progress_callback(content, exception): msg = content['message'].strip('\n') msg_queue.append(msg) final_msg = '\n'.join(msg_queue) if plugin: plugin_progress(job, msg) else: jail_progress(job, msg) job.set_progress(None, description=final_msg) def plugin_progress(job, msg): if 'Snapshotting' in msg: job.set_progress(20) elif 'Updating plugin INDEX' in msg: job.set_progress(40) elif 'Running upgrade' in msg: job.set_progress(70) elif 'Installing plugin packages' in msg: job.set_progress(90) elif f'{jail} successfully upgraded' in msg: job.set_progress(100) def jail_progress(job, msg): if 'Inspecting system' in msg: job.set_progress(20) elif 'Preparing to download files' in msg: job.set_progress(50) elif 'Applying patches' in msg: job.set_progress(75) elif 'Installing updates' in msg: job.set_progress(90) elif f'{jail} successfully upgraded' in msg: job.set_progress(100) _, _, iocage = self.check_jail_existence( jail, callback=progress_callback ) iocage.upgrade(release=release) return True
def create_job(self, job, options): verrors = ValidationErrors() uuid = options["uuid"] job.set_progress(0, f'Creating: {uuid}') try: self.check_jail_existence(uuid, skip=False) verrors.add('uuid', f'A jail with name {uuid} already exists') raise verrors except CallError: # A jail does not exist with the provided name, we can create one # now verrors = self.common_validation(verrors, options) if verrors: raise verrors job.set_progress(20, 'Initial validation complete') iocage = ioc.IOCage(skip_jails=True) release = options["release"] template = options.get("template", False) pkglist = options.get("pkglist", None) basejail = options["basejail"] empty = options["empty"] short = options["short"] props = options["props"] pool = IOCJson().json_get_value("pool") iocroot = IOCJson(pool).json_get_value("iocroot") if template: release = template if (not os.path.isdir(f'{iocroot}/releases/{release}') and not template and not empty): job.set_progress(50, f'{release} missing, calling fetch') self.middleware.call_sync('jail.fetch', {"release": release}, job=True) err, msg = iocage.create(release, props, 0, pkglist, template=template, short=short, _uuid=uuid, basejail=basejail, empty=empty) if err: raise CallError(msg) job.set_progress(100, f'Created: {uuid}') return True
def validate(self, value): verrors = ValidationErrors() for validator in self.validators: try: validator(value) except ValueError as e: verrors.add(self.name, str(e)) if verrors: raise verrors
def validate(self, value): verrors = ValidationErrors() for validator in self.validators: try: validator(value) except ShouldBe as e: verrors.add(self.name, f"Should be {e.what}") if verrors: raise verrors
def validate(self, value): verrors = ValidationErrors() for validator in self.validators: try: validator(value) except ShouldBe as e: verrors.add(self.name, f"Should be {e.what}") if verrors: raise verrors
def validate(self, value): verrors = ValidationErrors() for validator in self.validators: try: validator(value) except ValueError as e: verrors.add(self.name, str(e)) if verrors: raise verrors
def validate(self, value): verrors = ValidationErrors() for i, v in enumerate(value): for attr in self.items: try: attr.validate(v) except ValidationErrors as e: verrors.add_child(f"{self.name}.{i}", e) if verrors: raise verrors
def validate(self, value): verrors = ValidationErrors() for attr in self.attrs.values(): if attr.name in value: try: attr.validate(value[attr.name]) except ValidationErrors as e: verrors.add_child(self.name, e) if verrors: raise verrors
def validate(self, value): verrors = ValidationErrors() for i, v in enumerate(value): for attr in self.items: try: attr.validate(v) except ValidationErrors as e: verrors.add_child(f"{self.name}.{i}", e) if verrors: raise verrors
def validate(self, value): verrors = ValidationErrors() for attr in self.attrs.values(): if attr.name in value: try: attr.validate(value[attr.name]) except ValidationErrors as e: verrors.add_child(self.name, e) if verrors: raise verrors
def clean_and_validate_args(args, kwargs): args = list(args) args = args[:args_index] + copy.deepcopy(args[args_index:]) kwargs = copy.deepcopy(kwargs) verrors = ValidationErrors() # Iterate over positional args first, excluding self i = 0 for _ in args[args_index:]: attr = nf.accepts[i] value = attr.clean(args[args_index + i]) args[args_index + i] = value try: attr.validate(value) except ValidationErrors as e: verrors.extend(e) i += 1 # Use i counter to map keyword argument to rpc positional for x in list(range(i + 1, f.__code__.co_argcount)): kwarg = f.__code__.co_varnames[x] if kwarg in kwargs: attr = nf.accepts[i] i += 1 value = kwargs[kwarg] elif len(nf.accepts) >= i + args_index: attr = nf.accepts[i] i += 1 value = None else: i += 1 continue value = attr.clean(value) kwargs[kwarg] = value try: attr.validate(value) except ValidationErrors as e: verrors.extend(e) if verrors: raise verrors return args, kwargs
def validate(self, value): verrors = ValidationErrors() if value: if not os.path.exists(value): verrors.add(self.name, "This path does not exist.", errno.ENOENT) elif not os.path.isfile(value): verrors.add(self.name, "This path is not a file.", errno.EISDIR) if verrors: raise verrors return super().validate(value)
def validate(self, value): verrors = ValidationErrors() for attr in self.attrs.values(): if attr.name in value: try: attr.validate(value[attr.name]) except ValidationErrors as e: verrors.add_child(self.name, e) for v in value: if v not in Cron.FIELDS: verrors.add(self.name, f'Unexpected {v} value') if verrors: raise verrors cron_expression = '' for field in Cron.FIELDS: cron_expression += value.get(field) + ' ' if value.get( field) else '* ' try: croniter(cron_expression) except Exception as e: verrors.add(self.name, 'Please ensure fields match cron syntax - ' + str(e)) if verrors: raise verrors
def validate_return_type(func, result, schemas): if not schemas and result is None: return elif not schemas: raise ValueError(f'Return schema missing for {func.__name__!r}') result = copy.deepcopy(result) if not isinstance(result, tuple): result = [result] verrors = ValidationErrors() for res_entry, schema in zip(result, schemas): clean_and_validate_arg(verrors, schema, res_entry) verrors.check()
def validate(self, value): verrors = ValidationErrors() attr_verrors = ValidationErrors() for attr in self.schemas: try: attr.validate(value) except TypeError: pass except ValidationErrors as e: attr_verrors.extend(e) else: break else: verrors.extend(attr_verrors) verrors.check()
def do_update(self, jail, options): """Sets a jail property.""" plugin = options.pop("plugin") _, _, iocage = self.check_jail_existence(jail) name = options.pop("name", None) verrors = ValidationErrors() jail = self.query([['id', '=', jail]], {'get': True}) exclude_ips = [ ip.split('|')[1].split('/')[0] if '|' in ip else ip.split('/')[0] for f in ('ip4_addr', 'ip6_addr') for ip in jail[f].split(',') if ip != 'none' ] self.validate_ips( verrors, {'props': [f'{k}={v}' for k, v in options.items()]}, 'options', exclude_ips ) for prop, val in options.items(): p = f"{prop}={val}" try: iocage.set(p, plugin) except RuntimeError as err: raise CallError(err) if name: iocage.rename(name) return True
def validate(self, value): if value is None: return verrors = ValidationErrors() if value: if not os.path.exists(value): verrors.add(self.name, "This path does not exist.", errno.ENOENT) elif not os.path.isdir(value): verrors.add(self.name, "This path is not a directory.", errno.ENOTDIR) if verrors: raise verrors return super().validate(value)
def validate(self, value): verrors = ValidationErrors() for attr in self.attrs.values(): if attr.name in value: try: attr.validate(value[attr.name]) except ValidationErrors as e: verrors.add_child(self.name, e) for v in value: if v not in Cron.FIELDS: verrors.add(self.name, f'Unexpected {v} value') if verrors: raise verrors cron_expression = '' for field in Cron.FIELDS: cron_expression += value.get(field) + ' ' if value.get(field) else '* ' try: croniter(cron_expression) except Exception as e: verrors.add(self.name, 'Please ensure fields match cron syntax - ' + str(e)) if verrors: raise verrors
def get_attrs_to_skip(self, data): skip_attrs = defaultdict(set) check_data = self.get_defaults(data, {}, ValidationErrors(), False) if not self.update else data for attr, attr_data in filter( lambda k: not filter_list([check_data], k[1]['filters']), self.conditional_defaults.items() ): for k in attr_data['attrs']: skip_attrs[k].update({attr}) return skip_attrs
def clean_and_validate_args(args, kwargs): args = list(args) common_args = args[:args_index] signature_args = args[args_index:] had_warning = False for check, adapt in deprecated: if check(signature_args): if not had_warning: warnings.warn( f"Method {f!r} was called with a deprecated signature", DeprecationWarning) had_warning = True signature_args = adapt(*signature_args) args = common_args + copy.deepcopy(signature_args) kwargs = copy.deepcopy(kwargs) verrors = ValidationErrors() # Iterate over positional args first, excluding self i = 0 if len(args[args_index:]) > len(nf.accepts): raise CallError( f'Too many arguments (expected {len(nf.accepts)}, found {len(args[args_index:])})' ) for _ in args[args_index:]: args[args_index + i] = clean_and_validate_arg( verrors, nf.accepts[i], args[args_index + i]) i += 1 # Use i counter to map keyword argument to rpc positional for x in list(range(i + args_index, f.__code__.co_argcount)): kwarg = f.__code__.co_varnames[x] if kwarg in kwargs: attr = nf.accepts[i] i += 1 value = kwargs[kwarg] elif len(nf.accepts) >= i + 1: attr = nf.accepts[i] i += 1 value = NOT_PROVIDED else: i += 1 continue kwargs[kwarg] = clean_and_validate_arg(verrors, attr, value) if verrors: raise verrors return args, kwargs
def fetch(self, job, options): """Fetches a release or plugin.""" fetch_output = {'error': False, 'install_notes': []} verrors = ValidationErrors() self.validate_ips(verrors, options) def progress_callback(content): level = content['level'] msg = content['message'].strip('\n') if job.progress['percent'] == 90: for split_msg in msg.split('\n'): fetch_output['install_notes'].append(split_msg) if level == 'EXCEPTION': fetch_output['error'] = True raise CallError(msg) job.set_progress(None, msg) if ' These pkgs will be installed:' in msg: job.set_progress(50, msg) elif 'Installing plugin packages:' in msg: job.set_progress(75, msg) elif 'Command output:' in msg: job.set_progress(90, msg) self.check_dataset_existence() # Make sure our datasets exist. start_msg = None finaL_msg = None if options["name"] is not None: options["plugin_file"] = True start_msg = 'Starting plugin install' finaL_msg = f"Plugin: {options['name']} installed" options["accept"] = True iocage = ioc.IOCage(callback=progress_callback, silent=False) job.set_progress(0, start_msg) iocage.fetch(**options) if options['name'] is not None: # This is to get the admin URL and such fetch_output['install_notes'] += job.progress['description'].split( '\n') job.set_progress(100, finaL_msg) return fetch_output
def validate(self, value): super().validate(value) verrors = ValidationErrors() uri = urlparse(value) if not all(getattr(uri, k) for k in ('scheme', 'netloc')): verrors.add(self.name, 'Not a valid URI') verrors.check()
def validate(self, value): if value is None: return verrors = ValidationErrors() for attr in self.attrs.values(): if attr.name in value: try: attr.validate(value[attr.name]) except ValidationErrors as e: verrors.add_child(self.name, e) for v in value: if self.begin_end and v in ['begin', 'end']: continue if v not in Cron.FIELDS: verrors.add(self.name, f'Unexpected {v} value') if verrors: raise verrors cron_expression = '' for field in Cron.FIELDS: cron_expression += value.get(field) + ' ' if value.get( field) else '* ' try: croniter(cron_expression) except Exception as e: verrors.add(self.name, 'Please ensure fields match cron syntax - ' + str(e)) if value.get('begin') and value.get('end') and not ( value.get('begin') <= value.get('end')): verrors.add(self.name, 'Begin time should be less or equal than end time') if verrors: raise verrors
def clean(self, data): data = super().clean(data) if data is None: if self.null: return None return copy.deepcopy(self.default) if not isinstance(data, dict): raise Error(self.name, 'A dict was expected') verrors = ValidationErrors() for key, value in list(data.items()): if not self.additional_attrs: if key not in self.attrs: verrors.add(f'{self.name}.{key}', 'Field was not expected') continue attr = self.attrs.get(key) if not attr: continue data[key] = self._clean_attr(attr, value, verrors) # Do not make any field and required and not populate default values if not self.update: data.update(self.get_defaults(data, self.get_attrs_to_skip(data), verrors)) verrors.check() return data
def _do_create(self, job, data): self.middleware.call_sync('jail.check_dataset_existence') verrors = ValidationErrors() branch = data.pop('branch') or self.get_version() install_notes = '' plugin_name = data.pop('plugin_name') jail_name = data.pop('jail_name') plugin_repository = data.pop('plugin_repository') post_install = False job.set_progress(0, f'Creating plugin: {plugin_name}') if jail_name in [ j['id'] for j in self.middleware.call_sync('jail.query') ]: verrors.add('plugin_create.jail_name', f'A jail with name {jail_name} already exists') else: verrors = common_validation(self.middleware, data, schema='plugin_create') verrors.check() job.set_progress(20, 'Initial validation complete') def progress_callback(content, exception): msg = content['message'].strip('\r\n') nonlocal install_notes, post_install if post_install and msg: install_notes += f'\n{msg}' if ' These pkgs will be installed:' in msg: job.set_progress(50, msg) elif 'Installing plugin packages:' in msg: job.set_progress(75, msg) elif 'Running post_install.sh' in msg: job.set_progress(90, msg) # Sets each message going forward as important to the user post_install = True else: job.set_progress(None, msg) ioc.IOCage(callback=progress_callback, silent=False).fetch( **{ 'accept': True, 'name': jail_name, 'plugin_name': plugin_name, 'git_repository': plugin_repository, 'props': data['props'], 'branch': branch, }) new_plugin = self.middleware.call_sync('plugin._get_instance', jail_name) new_plugin['install_notes'] = install_notes.strip() return new_plugin
def clone(self, job, source_jail, options): verrors = ValidationErrors() try: self.check_jail_existence(source_jail, skip=False) except CallError: verrors.add('source_jail', f'{source_jail} does not exist.', errno.ENOENT) else: try: self.check_jail_existence(options['uuid'], skip=False) except CallError: pass else: verrors.add( 'clone_jail.uuid', f'Jail with "{options["uuid"]}" uuid already exists.', errno.EEXIST) verrors.check() verrors = common_validation(self.middleware, options, schema='clone_jail') verrors.check() job.set_progress(25, 'Initial validation complete.') ioc.IOCage(jail=source_jail, skip_jails=True).create(source_jail, options['props'], _uuid=options['uuid'], thickjail=options['thickjail'], clone=True) job.set_progress(100, 'Jail has been successfully cloned.') return self.middleware.call_sync('jail._get_instance', options['uuid'])
def validate(self, value): verrors = ValidationErrors() if value: try: if self.cidr: self.factory(value, strict=self.cidr_strict) else: has_zone_index = False if self.allow_zone_index and "%" in value: has_zone_index = True value = value[:value.rindex("%")] addr = self.factory(value) if has_zone_index and not isinstance(addr, ipaddress.IPv6Address): raise ValueError("Zone index is allowed only for IPv6 addresses") except ValueError as e: verrors.add(self.name, str(e), errno.EINVAL) if verrors: raise verrors return super().validate(value)
def validate(self, value): if value is None: return verrors = ValidationErrors() s = set() for i, v in enumerate(value): if self.unique: if isinstance(v, dict): v = tuple(sorted(list(v.items()))) if v in s: verrors.add(f"{self.name}.{i}", "This value is not unique.") s.add(v) for attr in self.items: try: attr.validate(v) except ValidationErrors as e: verrors.add_child(f"{self.name}.{i}", e) if verrors: raise verrors super().validate(value)
def clean_and_validate_args(args, kwargs): args = list(args) args = args[:args_index] + copy.deepcopy(args[args_index:]) kwargs = copy.deepcopy(kwargs) verrors = ValidationErrors() def clean_and_validate_arg(attr, arg): try: value = attr.clean(arg) attr.validate(value) return value except Error as e: verrors.add(e.attribute, e.errmsg, e.errno) except ValidationErrors as e: verrors.extend(e) # Iterate over positional args first, excluding self i = 0 for _ in args[args_index:]: args[args_index + i] = clean_and_validate_arg( nf.accepts[i], args[args_index + i]) i += 1 # Use i counter to map keyword argument to rpc positional for x in list(range(i + args_index, f.__code__.co_argcount)): kwarg = f.__code__.co_varnames[x] if kwarg in kwargs: attr = nf.accepts[i] i += 1 value = kwargs[kwarg] elif len(nf.accepts) >= i + 1: attr = nf.accepts[i] i += 1 value = NOT_PROVIDED else: i += 1 continue kwargs[kwarg] = clean_and_validate_arg(attr, value) if verrors: raise verrors return args, kwargs
def validate(self, value): if value is None: return verrors = ValidationErrors() if not dn.is_dn(value): verrors.add(self.name, "Invalid LDAP DN specified.") verrors.check() return super().validate(value)
async def check_disks_availability(self, disks, allow_duplicate_serials): """ Makes sure the disks are present in the system and not reserved by anything else (boot, pool, iscsi, etc). Returns: verrors, dict - validation errors (if any) and disk.query for all disks """ verrors = ValidationErrors() disks_cache = dict(map(lambda x: (x['devname'], x), await self.middleware.call('disk.query'))) disks_set = set(disks) disks_not_in_cache = disks_set - set(disks_cache.keys()) if disks_not_in_cache: verrors.add( 'topology', f'The following disks were not found in system: {"," .join(disks_not_in_cache)}.' ) disks_reserved = await self.middleware.call('disk.get_reserved') already_used = disks_set - (disks_set - set(disks_reserved)) if already_used: verrors.add( 'topology', f'The following disks are already in use: {"," .join(already_used)}.' ) if not allow_duplicate_serials and not verrors: serial_to_disk = defaultdict(set) for disk in disks: serial_to_disk[(disks_cache[disk]['serial'], disks_cache[disk]['lunid'])].add(disk) for reserved_disk in disks_reserved: reserved_disk_cache = disks_cache.get(reserved_disk) if not reserved_disk_cache: continue serial_to_disk[(reserved_disk_cache['serial'], reserved_disk_cache['lunid'])].add(reserved_disk) if duplicate_serials := {serial for serial, serial_disks in serial_to_disk.items() if len(serial_disks) > 1}: error = ', '.join(map(lambda serial: f'{serial[0]!r} ({", ".join(sorted(serial_to_disk[serial]))})', duplicate_serials)) verrors.add('topology', f'Disks have duplicate serial numbers: {error}.')
def validate(self, value): if value is None: return value verrors = ValidationErrors() if value and len(str(value)) > self.max_length: verrors.add(self.name, f'Value greater than {self.max_length} not allowed') verrors.check() return super().validate(value)
def clean_and_validate_args(args, kwargs): args = list(args) args = args[:args_index] + copy.deepcopy(args[args_index:]) kwargs = copy.deepcopy(kwargs) verrors = ValidationErrors() # Iterate over positional args first, excluding self i = 0 for _ in args[args_index:]: attr = nf.accepts[i] value = attr.clean(args[args_index + i]) args[args_index + i] = value try: attr.validate(value) except ValidationErrors as e: verrors.extend(e) i += 1 # Use i counter to map keyword argument to rpc positional for x in list(range(i + 1, f.__code__.co_argcount)): kwarg = f.__code__.co_varnames[x] if kwarg in kwargs: attr = nf.accepts[i] i += 1 value = kwargs[kwarg] elif len(nf.accepts) >= i + args_index: attr = nf.accepts[i] i += 1 value = None else: i += 1 continue value = attr.clean(value) kwargs[kwarg] = value try: attr.validate(value) except ValidationErrors as e: verrors.extend(e) if verrors: raise verrors return args, kwargs
def validate(self, value): verrors = ValidationErrors() if value: if not os.path.exists(value): verrors.add(self.name, "This path does not exist.", errno.ENOENT) elif not os.path.isfile(value): verrors.add(self.name, "This path is not a file.", errno.EISDIR) if verrors: raise verrors return super().validate(value)
async def validate(self, data, dev): verrors = ValidationErrors() unavail = [ i for i in data['capabilities'] if i not in dev.supported_capabilities ] if unavail: # gave us a capability that isn't supported on the device # or is "fixed" (meaning it can't be changed) verrors.add( f'capabilities_set.{data["action"]}', f'"{data["name"]}" does not support "{", ".join(unavail)}"') verrors.check()
def validate(self, value): if value is None: return verrors = ValidationErrors() for attr in self.attrs.values(): if attr.name in value: try: attr.validate(value[attr.name]) except ValidationErrors as e: verrors.add_child(self.name, e) for v in value: if self.begin_end and v in ['begin', 'end']: continue if v not in Cron.FIELDS: verrors.add(self.name, f'Unexpected {v} value') if verrors: raise verrors cron_expression = '' for field in Cron.FIELDS: cron_expression += value.get(field) + ' ' if value.get(field) else '* ' try: croniter(cron_expression) except Exception as e: verrors.add(self.name, 'Please ensure fields match cron syntax - ' + str(e)) if value.get('begin') and value.get('end') and not (value.get('begin') <= value.get('end')): verrors.add(self.name, 'Begin time should be less or equal than end time') if verrors: raise verrors