class DeviceForm(ObjectForm): form_type = HiddenField(default="device") icon = SelectField( "Icon", choices=( ("antenna", "Antenna"), ("firewall", "Firewall"), ("host", "Host"), ("optical_switch", "Optical switch"), ("regenerator", "Regenerator"), ("router", "Router"), ("server", "Server"), ("switch", "Switch"), ), ) ip_address = StringField("IP address") port = IntegerField("Port", default=22) operating_system = StringField("Operating System") os_version = StringField("OS Version") longitude = StringField("Longitude", default=0.0) latitude = StringField("Latitude", default=0.0) napalm_driver = SelectField("NAPALM Driver", choices=vs.napalm_drivers, default="ios") netmiko_driver = SelectField("Netmiko Driver", choices=vs.netmiko_drivers, default="cisco_ios") scrapli_driver = SelectField( "Scrapli Driver", choices=vs.dualize(vs.scrapli_drivers), default="cisco_iosxe", ) netconf_driver = SelectField("Netconf Driver", choices=vs.netconf_drivers, default="default")
class GenericFileTransferForm(ServiceForm): form_type = HiddenField(default="generic_file_transfer_service") direction = SelectField(choices=(("get", "Get"), ("put", "Put"))) protocol = SelectField(choices=(("scp", "SCP"), ("sftp", "SFTP"))) source_file = StringField(validators=[InputRequired()], substitution=True) destination_file = StringField(validators=[InputRequired()], substitution=True) missing_host_key_policy = BooleanField() load_known_host_keys = BooleanField() source_file_includes_globbing = BooleanField( "Source file includes glob pattern") max_transfer_size = IntegerField(default=2**30) window_size = IntegerField(default=2**30) timeout = FloatField(default=10.0) credentials = SelectField( "Credentials", choices=( ("device", "Device Credentials"), ("user", "User Credentials"), ("custom", "Custom Credentials"), ), ) custom_username = StringField("Custom Username", substitution=True) custom_password = PasswordField("Custom Password", substitution=True) def validate(self): valid_form = super().validate() invalid_direction = (self.source_file_includes_globbing.data and self.direction.data == "get") if invalid_direction: self.direction.errors.append( "Globbing only works with the 'PUT' direction") return valid_form and not invalid_direction
def generate_filtering_forms(self): for model, properties in vs.properties["filtering"].items(): relations = {} for related_model, relation in vs.relationships[model].items(): if related_model in ("edges", "results"): continue relations[related_model] = MultipleInstanceField( related_model, model=related_model) vs.relationships[f"{model}_filtering"][ related_model] = relation filtering_key = f"{model}_relation_filtering" vs.relationships[filtering_key][related_model] = relation relation_form = { "template": "filtering", "properties": sorted(relations), "object_type": model, "form_type": HiddenField(default=f"{model}_relation_filtering"), **{ **relations, **{ f"{relation}_filter": SelectField(choices=( ("union", "Union"), ("intersection", "Intersection"), ("empty", "Empty"), )) for relation in relations }, }, } type(f"{model}RelationshipFilteringForm", (BaseForm, ), relation_form) form, form_type = deepcopy(relation_form), f"{model}_filtering" for property in properties: vs.form_properties[form_type][f"{property}_filter"] = { "type": "list" } form.update({ "form_type": HiddenField(default=form_type), "properties": sorted(properties) + sorted(relations), **{property: StringField() for property in properties}, **{ f"{property}_filter": SelectField(choices=( ("inclusion", "Inclusion"), ("equality", "Equality"), ("regex", "Regular Expression"), ("empty", "Empty"), )) for property in properties }, }) type(f"{model}FilteringForm", (BaseForm, ), form)
class TaskForm(BaseForm): action = "eNMS.base.processData" form_type = HiddenField(default="task") id = HiddenField() name = StringField("Name", [InputRequired()]) default_access = SelectField(choices=( ("creator", "Creator only"), ("public", "Public (all users)"), ("admin", "Admin Users only"), )) scheduling_mode = SelectField( "Scheduling Mode", choices=(("cron", "Crontab Scheduling"), ("standard", "Standard Scheduling")), ) description = StringField("Description") start_date = StringField("Start Date", type="date") end_date = StringField("End Date", type="date") frequency = IntegerField("Frequency", default=0) frequency_unit = SelectField( "Frequency Unit", choices=( ("seconds", "Seconds"), ("minutes", "Minutes"), ("hours", "Hours"), ("days", "Days"), ), ) crontab_expression = StringField("Crontab Expression") initial_payload = DictField("Payload") devices = MultipleInstanceField("Devices", model="device") pools = MultipleInstanceField("Pools", model="pool") service = InstanceField("Service", model="service") def validate(self): valid_form = super().validate() if self.name.data == "Bulk Edit": return valid_form no_date = self.scheduling_mode.data == "standard" and not self.start_date.data if no_date: self.start_date.errors.append("A start date must be set.") no_cron_expression = (self.scheduling_mode.data == "cron" and not self.crontab_expression.data) if no_cron_expression: self.crontab_expression.errors.append( "A crontab expression must be set.") no_service = not self.service.data if no_service: self.service.errors.append("No service set.") return valid_form and not any( [no_date, no_cron_expression, no_service])
class UserForm(RbacForm): form_type = HiddenField(default="user") groups = StringField("Groups") theme = SelectField( "Theme", choices=[(theme, values["name"]) for theme, values in vs.themes["themes"].items()], ) authentication = SelectField( "Authentication", choices=[(method, values["display_name"]) for method, values in vs.settings["authentication"]["methods"].items()], ) password = PasswordField("Password") is_admin = BooleanField(default=False)
class RestartWorkflowForm(BaseForm): action = "eNMS.workflowBuilder.restartWorkflow" form_type = HiddenField(default="restart_workflow") start_services = HiddenField() restart_runtime = SelectField("Restart Runtime", validate_choice=False) targets = SelectField( "Targets", choices=( ("Manually defined", "Use the devices manually defined below."), ("Restart run", "Use the targets from the restart run."), ("Workflow", "Use the targets defined at workflow level."), ), ) restart_devices = MultipleInstanceField("Devices", model="device") restart_pools = MultipleInstanceField("Pools", model="pool")
def form_init(cls): cls.models = ("device", "link", "service", "user") for model in cls.models: setattr(cls, f"{model}_properties", vs.properties["filtering"][model]) for property in vs.properties["filtering"][model]: setattr(cls, f"{model}_{property}", StringField(property)) setattr(cls, f"{model}_{property}_invert", BooleanField(property)) vs.form_properties["pool"][f"{model}_{property}_match"] = { "type": "list" } vs.form_properties["pool"][f"{model}_{property}_invert"] = { "type": "bool" } setattr( cls, f"{model}_{property}_match", SelectField(choices=( ("inclusion", "Inclusion"), ("equality", "Equality"), ("regex", "Regular Expression"), ("empty", "Empty"), )), )
class ConnectionForm(ServiceForm): form_type = HiddenField(default="connection") get_request_allowed = False abstract_service = True credentials = SelectField( "Credentials", choices=( ("device", "Device Credentials"), ("user", "User Credentials"), ("custom", "Custom Credentials"), ), ) custom_username = StringField("Custom Username", substitution=True) custom_password = PasswordField("Custom Password", substitution=True) start_new_connection = BooleanField("Start New Connection") connection_name = StringField("Connection Name", default="default") close_connection = BooleanField("Close Connection") groups = { "Connection Parameters": { "commands": [ "credentials", "custom_username", "custom_password", "start_new_connection", "connection_name", "close_connection", ], "default": "expanded", } }
class NetmikoFileTransferForm(NetmikoForm): form_type = HiddenField(default="netmiko_file_transfer_service") source_file = StringField(validators=[InputRequired()], substitution=True) destination_file = StringField(validators=[InputRequired()], substitution=True) file_system = StringField() direction = SelectField(choices=(("put", "Upload"), ("get", "Download"))) disable_md5 = BooleanField() inline_transfer = BooleanField() overwrite_file = BooleanField() groups = { "Main Parameters": { "commands": [ "source_file", "destination_file", "file_system", "direction", "disable_md5", "inline_transfer", "overwrite_file", ], "default": "expanded", }, **NetmikoForm.groups, }
class WorkflowLabelForm(BaseForm): form_type = HiddenField(default="workflow_label") action = "eNMS.workflowBuilder.createLabel" text = StringField(widget=TextArea(), render_kw={"rows": 15}) alignment = SelectField( "Text Alignment", choices=(("left", "Left"), ("center", "Center"), ("right", "Right")), )
class DeviceConnectionForm(BaseForm): template = "device_connection" form_type = HiddenField(default="device_connection") address_choices = [("ip_address", "IP address"), ("name", "Name")] + [ (property, values["pretty_name"]) for property, values in vs.properties["custom"]["device"].items() if values.get("is_address", False) ] address = SelectField(choices=address_choices) username = StringField("Username") password = PasswordField("Password")
class AddServiceForm(BaseForm): form_type = HiddenField(default="add_services_to_workflow") template = "add_services_to_workflow" mode = SelectField( "Mode", choices=( ("deep", "Deep Copy (creates a duplicate from the service)"), ("shallow", "Shallow Copy (creates a reference to the service)"), ), ) search = StringField()
class LoginForm(BaseForm): form_type = HiddenField(default="login") get_request_allowed = False authentication_method = SelectField( "Authentication Method", choices=[(method, properties["display_name"]) for method, properties in vs.settings["authentication"]["methods"].items() if properties["enabled"]], ) username = StringField("Name", [InputRequired()]) password = PasswordField("Password", [InputRequired()])
class DebugForm(BaseForm): template = "debug" form_type = HiddenField(default="debug") snippets = SelectField(validate_choice=False) code = StringField( "Python Code", type="code", python=True, widget=TextArea(), render_kw={"rows": 15}, ) output = StringField("Output", widget=TextArea(), render_kw={"rows": 16})
class NapalmConfigurationForm(NapalmForm): form_type = HiddenField(default="napalm_configuration_service") action = SelectField( choices=( ("load_merge_candidate", "Load merge"), ("load_replace_candidate", "Load replace"), ) ) content = StringField(widget=TextArea(), render_kw={"rows": 5}, substitution=True) groups = { "Main Parameters": {"commands": ["action", "content"], "default": "expanded"}, **NapalmForm.groups, }
class AnsiblePlaybookForm(ServiceForm): form_type = HiddenField(default="ansible_playbook_service") playbook_path = SelectField("Playbook Path", validate_choice=False) arguments = StringField( "Arguments (Ansible command line options)", substitution=True, help="ansible/arguments", ) pass_device_properties = BooleanField( "Pass Device Inventory Properties (to be used " "in the playbook as {{name}} or {{ip_address}})") credentials = SelectField( "Credentials", choices=( ("device", "Device Credentials"), ("user", "User Credentials"), ), ) options = DictField( "Options (passed to ansible as -e extra args)", substitution=True, help="ansible/options", )
class ScrapliForm(ConnectionForm): form_type = HiddenField(default="scrapli_service") commands = StringField(substitution=True, widget=TextArea(), render_kw={"rows": 5}) is_configuration = BooleanField() driver = SelectField(choices=vs.dualize(vs.scrapli_drivers)) transport = SelectField(choices=vs.dualize(("system", "paramiko", "ssh2"))) use_device_driver = BooleanField(default=True) groups = { "Main Parameters": { "commands": [ "commands", "is_configuration", "driver", "transport", "use_device_driver", ], "default": "expanded", }, **ConnectionForm.groups, }
class ScrapliNetconfForm(ConnectionForm): form_type = HiddenField(default="scrapli_netconf_service") command = SelectField(choices=( ("get", "Get"), ("rpc", "RPC"), ("get_config", "Get Configuration"), ("edit_config", "Edit Configuration"), ("delete_config", "Delete Configuration"), ("commit", "Commit Configuration"), ("discard", "Discard Configuration"), ("lock", "Lock"), ("unlock", "Unlock"), )) target = SelectField(choices=( ("running", "Running Configuration"), ("startup", "Startup Configuration"), ("candidate", "Candidate Configuration"), )) content = StringField(substitution=True, widget=TextArea(), render_kw={"rows": 5}) commit_config = BooleanField("Commit After Editing Configuration") strip_namespaces = BooleanField("Strip Namespaces from returned XML") groups = { "Main Parameters": { "commands": [ "command", "target", "content", "commit_config", "strip_namespaces", ], "default": "expanded", }, **ConnectionForm.groups, }
class CredentialForm(BaseForm): action = "eNMS.base.processData" form_type = HiddenField(default="credential") id = HiddenField() name = StringField("Name", [InputRequired()]) description = StringField(widget=TextArea(), render_kw={"rows": 6}) role = SelectField( "Role", choices=( ("read-write", "Read Write"), ("read-only", "Read Only"), ), ) subtype = SelectField( "Subtype", choices=(("password", "Username / Password"), ("key", "SSH Key")), ) device_pools = MultipleInstanceField("Devices", model="pool") user_pools = MultipleInstanceField("Users", model="pool") priority = IntegerField("Priority", default=1) username = StringField("Username") password = PasswordField("Password") private_key = StringField(widget=TextArea(), render_kw={"rows": 1}) enable_password = PasswordField("'Enable' Password")
class ChangelogForm(BaseForm): action = "eNMS.base.processData" form_type = HiddenField(default="changelog") id = HiddenField() severity = SelectField( "Severity", choices=( ("debug", "Debug"), ("info", "Info"), ("warning", "Warning"), ("error", "Error"), ("critical", "Critical"), ), ) content = StringField(widget=TextArea(), render_kw={"rows": 10})
class PingForm(ServiceForm): form_type = HiddenField(default="ping_service") protocol = SelectField(choices=(("ICMP", "ICMP Ping"), ("TCP", "TCP Ping"))) ports = StringField("Ports (TCP ping only)", default=22) count = IntegerField(default=5) timeout = IntegerField(default=2) ttl = IntegerField(default=60) packet_size = IntegerField(default=56) def validate(self): valid_form = super().validate() invalid_tcp_port = self.protocol.data == "TCP" and not self.ports.data if invalid_tcp_port: self.ports.errors.append("You must enter a port for a TCP ping.") return valid_form and not invalid_tcp_port
class WorkflowForm(ServiceForm): form_type = HiddenField(default="workflow") close_connection = BooleanField(default=False) run_method = SelectField( "Run Method", choices=( ("per_device", "Run the workflow device by device"), ( "per_service_with_workflow_targets", "Run the workflow service by service using workflow targets", ), ( "per_service_with_service_targets", "Run the workflow service by service using service targets", ), ), ) superworkflow = InstanceField("Superworkflow")
class NapalmForm(ConnectionForm): form_type = HiddenField(default="napalm") get_request_allowed = False abstract_service = True driver = SelectField(choices=vs.napalm_drivers) use_device_driver = BooleanField( default=True, help="common/use_device_driver", ) timeout = IntegerField(default=10) optional_args = DictField() groups = { "Napalm Parameters": { "commands": ["driver", "use_device_driver", "timeout", "optional_args"], "default": "expanded", }, **ConnectionForm.groups, }
class NapalmBackupForm(NapalmForm): form_type = HiddenField(default="napalm_backup_service") property = SelectField( "Configuration Property to Update", choices=list(vs.configuration_properties.items()), ) getters = SelectMultipleField(choices=vs.automation["napalm"]["getters"]) replacements = FieldList(FormField(ReplacementForm), min_entries=3) groups = { "Target Property and Getters": { "commands": ["property", "getters"], "default": "expanded", }, "Search Response & Replace": { "commands": ["replacements"], "default": "expanded", }, **NapalmForm.groups, }
class DataBackupForm(NetmikoForm): form_type = HiddenField(default="netmiko_backup_service") property = SelectField( "Configuration Property to Update", choices=list(vs.configuration_properties.items()), ) commands = FieldList(FormField(CommandsForm), min_entries=12) replacements = FieldList(FormField(ReplacementForm), min_entries=12) add_header = BooleanField("Add header for each ommand", default=True) groups = { "Target property and commands": { "commands": ["property", "add_header", "commands"], "default": "expanded", }, "Search Response & Replace": { "commands": ["replacements"], "default": "expanded", }, **NetmikoForm.groups, }
class DataExtractionForm(ServiceForm): form_type = HiddenField(default="data_extraction_service") variable1 = StringField("Variable Name") query1 = StringField("Python Extraction Query", python=True) match_type1 = SelectField("Post Processing", choices=match_choices) match1 = StringField( "Regular Expression / TextFSM Template Text", widget=TextArea(), render_kw={"rows": 5}, ) operation1 = SelectField("Operation", choices=operation_choices) variable2 = StringField("Variable Name") query2 = StringField("Python Extraction Query", python=True) match_type2 = SelectField("Post Processing", choices=match_choices) match2 = StringField( "Regular Expression / TextFSM Template Text", widget=TextArea(), render_kw={"rows": 5}, ) operation2 = SelectField("Operation", choices=operation_choices) variable3 = StringField("Variable Name") query3 = StringField("Python Extraction Query", python=True) match_type3 = SelectField("Post Processing", choices=match_choices) match3 = StringField( "Regular Expression / TextFSM Template Text", widget=TextArea(), render_kw={"rows": 5}, ) operation3 = SelectField("Operation", choices=operation_choices) groups = { "Extraction 1": { "commands": ["variable1", "query1", "match_type1", "match1", "operation1"], "default": "expanded", }, "Extraction 2": { "commands": ["variable2", "query2", "match_type2", "match2", "operation2"], "default": "expanded", }, "Extraction 3": { "commands": ["variable3", "query3", "match_type3", "match3", "operation3"], "default": "expanded", }, }
class TopologyImportForm(ServiceForm): form_type = HiddenField(default="topology_import_service") import_type = SelectField(choices=( ("librenms", "LibreNMS"), ("netbox", "Netbox"), ("opennms", "OpenNMS"), )) netbox_address = StringField(default="http://0.0.0.0:8000") netbox_token = PasswordField() opennms_address = StringField() opennms_devices = StringField() opennms_login = StringField() opennms_password = PasswordField() librenms_address = StringField(default="http://librenms.example.com") librenms_token = PasswordField() groups = { "Type of Import": { "commands": ["import_type"], "default": "expanded" }, "Netbox": { "commands": ["netbox_address", "netbox_token"], "default": "expanded", }, "OpenNMS": { "commands": [ "opennms_address", "opennms_devices", "opennms_login", "opennms_password", ], "default": "expanded", }, "LibreNMS": { "commands": ["librenms_address", "librenms_token"], "default": "expanded", }, }
class NetmikoForm(ConnectionForm): form_type = HiddenField(default="netmiko") abstract_service = True driver = SelectField(choices=vs.netmiko_drivers) use_device_driver = BooleanField( default=True, help="common/use_device_driver", ) enable_mode = BooleanField("Enable mode (run in enable mode or as root)", default=True) config_mode = BooleanField( "Config mode (See Advanced Parameters to override the config mode command)", default=False, ) fast_cli = BooleanField() timeout = FloatField(default=10.0) delay_factor = FloatField( ("Delay Factor (Changing from default of 1" " will nullify Netmiko Timeout setting)"), default=1.0, ) global_delay_factor = FloatField( ("Global Delay Factor (Changing from default of 1" " will nullify Netmiko Timeout setting)"), default=1.0, ) jump_on_connect = BooleanField( "Jump to remote device on connect", default=False, help="netmiko/jump_on_connect", ) jump_command = StringField( label="Command that jumps to device", default="ssh jump_server_IP", substitution=True, help="netmiko/jump_command", ) jump_username = StringField(label="Device username", substitution=True, help="netmiko/jump_username") jump_password = PasswordField(label="Device password", substitution=True, help="netmiko/jump_password") exit_command = StringField( label="Command to exit device back to original device", default="exit", substitution=True, help="netmiko/exit_command", ) expect_username_prompt = StringField( "Expected username prompt", default="username:"******"netmiko/expect_username_prompt", ) expect_password_prompt = StringField( "Expected password prompt", default="password", substitution=True, help="netmiko/expect_password_prompt", ) expect_prompt = StringField( "Expected prompt after login", default="admin.*$", substitution=True, help="netmiko/expect_prompt", ) groups = { "Netmiko Parameters": { "commands": [ "driver", "use_device_driver", "enable_mode", "config_mode", "fast_cli", "timeout", "delay_factor", "global_delay_factor", ], "default": "expanded", }, **ConnectionForm.groups, "Jump on connect Parameters": { "commands": [ "jump_on_connect", "jump_command", "expect_username_prompt", "jump_username", "expect_password_prompt", "jump_password", "expect_prompt", "exit_command", ], "default": "hidden", }, }
class DeviceDataForm(BaseForm): template = "device_data" form_type = HiddenField(default="device_data") data_type = SelectField("Display", choices=vs.configuration_properties)
class ServiceForm(BaseForm): template = "service" form_type = HiddenField(default="service") get_request_allowed = False id = HiddenField() name = StringField("Name") type = StringField("Service Type") access_groups = StringField("Groups") shared = BooleanField("Shared") scoped_name = StringField("Scoped Name", [InputRequired()]) description = StringField("Description") device_query = StringField("Device Query", python=True, widget=TextArea(), render_kw={"rows": 2}) device_query_property = SelectField("Query Property Type", choices=(("name", "Name"), ("ip_address", "IP address"))) target_devices = MultipleInstanceField("Devices", model="device") disable_result_creation = BooleanField("Save only failed results") target_pools = MultipleInstanceField("Pools", model="pool") update_target_pools = BooleanField("Update target pools before running") update_pools_after_running = BooleanField("Update pools after running") workflows = MultipleInstanceField("Workflows", model="workflow") owners = MultipleInstanceField("Owners", model="user") owners_access = SelectMultipleStringField( "Owners Access", choices=[("run", "Run"), ("edit", "Edit")], ) waiting_time = IntegerField( "Time to Wait before next service is started (in seconds)", default=0) priority = IntegerField("Priority", default=1) send_notification = BooleanField("Send a notification") send_notification_method = SelectField( "Notification Method", choices=(("mail", "Mail"), ("slack", "Slack"), ("mattermost", "Mattermost")), ) notification_header = StringField(widget=TextArea(), render_kw={"rows": 5}, substitution=True) include_device_results = BooleanField("Include Device Results") include_link_in_summary = BooleanField("Include Result Link in Summary") display_only_failed_nodes = BooleanField("Display only Failed Devices") mail_recipient = StringField("Mail Recipients (separated by comma)") reply_to = StringField("Reply-to Email Address") number_of_retries = IntegerField("Number of retries", default=0) time_between_retries = IntegerField("Time between retries (in seconds)", default=10) max_number_of_retries = IntegerField("Maximum number of retries", default=100) credential_type = SelectField( "Type of Credentials", choices=( ("any", "Any"), ("read-write", "Read Write"), ("read-only", "Read Only"), ), ) maximum_runs = IntegerField("Maximum number of runs", default=1) skip_query = StringField("Skip Query (Python)", python=True, widget=TextArea(), render_kw={"rows": 2}) skip_value = SelectField( "Skip Value", choices=( ("success", "Success"), ("failure", "Failure"), ("discard", "Discard"), ), ) vendor = StringField("Vendor") operating_system = StringField("Operating System") iteration_values = StringField("Iteration Values", python=True) initial_payload = DictField() mandatory_parametrization = BooleanField("Parameterized Form is Mandatory") parameterized_form = StringField( type="code", python=True, widget=TextArea(), default="\n".join(vs.automation["parameterized_form"]), ) iteration_variable_name = StringField("Iteration Variable Name", default="iteration_value") iteration_devices = StringField("Iteration Devices", python=True) iteration_devices_property = SelectField( "Iteration Devices Property", choices=(("name", "Name"), ("ip_address", "IP address")), ) preprocessing = StringField(type="code", python=True, widget=TextArea()) postprocessing = StringField(type="code", python=True, widget=TextArea()) postprocessing_mode = SelectField(choices=( ("success", "Run on success only"), ("failure", "Run on failure only"), ("always", "Always run"), )) default_access = SelectField(choices=( ("creator", "Role Based (Creator)"), ("public", "Public (All users)"), ("admin", "Admin (Admins only"), )) log_level = SelectField( "Logging", choices=((0, "Disable logging"), *enumerate(vs.log_levels, 1)), default=1, ) multiprocessing = BooleanField("Multiprocessing") max_processes = IntegerField("Maximum number of processes", default=15) validation_condition = SelectField(choices=( ("none", "No validation"), ("success", "Run on success only"), ("failure", "Run on failure only"), ("always", "Always run"), )) conversion_method = SelectField(choices=( ("none", "No conversion"), ("text", "Text"), ("json", "Json dictionary"), ("xml", "XML dictionary"), )) validation_method = SelectField( "Validation Method", choices=( ("text", "Validation by text match"), ("dict_included", "Validation by dictionary inclusion"), ("dict_equal", "Validation by dictionary equality"), ), ) validation_section = StringField("Section to Validate", default="results['result']") content_match = StringField("Content Match", widget=TextArea(), render_kw={"rows": 8}, substitution=True) content_match_regex = BooleanField( '"Content Match" is a regular expression') dict_match = DictField("Dictionary to Match Against", substitution=True) negative_logic = BooleanField("Negative logic") delete_spaces_before_matching = BooleanField( "Delete Spaces before Matching") run_method = SelectField( "Run Method", choices=( ("per_device", "Run the service once per device"), ("once", "Run the service once"), ), ) def validate(self): valid_form = super().validate() no_recipient_error = (self.send_notification.data and self.send_notification_method.data == "mail" and not self.mail_recipient.data) if no_recipient_error: self.mail_recipient.errors.append( "Please add at least one recipient for the mail notification.") forbidden_name_error = self.scoped_name.data in ("Start", "End", "Placeholder") if forbidden_name_error: self.name.errors.append("This name is not allowed.") conversion_validation_mismatch = self.validation_condition.data != "none" and ( self.conversion_method.data == "text" and "dict" in self.validation_method.data or self.conversion_method.data in ("xml", "json") and "dict" not in self.validation_method.data) if conversion_validation_mismatch: self.conversion_method.errors.append( f"The conversion method is set to {self.conversion_method.data}" f" and the validation method to {self.validation_method.data} :" " these do not match.") empty_validation = self.validation_condition.data != "none" and ( self.validation_method.data == "text" and not self.content_match.data or self.validation_method.data == "dict_included" and self.dict_match.data == "{}") if empty_validation: self.content_match.errors.append( f"The validation method is set to '{self.validation_method.data}'" f" and the matching value is empty: these do no match.") too_many_threads_error = (self.max_processes.data > vs.settings["automation"]["max_process"]) if too_many_threads_error: self.max_processes.errors.append( "The number of threads used for multiprocessing must be " f"less than {vs.settings['automation']['max_process']}.") shared_service_error = not self.shared.data and len( self.workflows.data) > 1 if shared_service_error: self.shared.errors.append( "The 'shared' property is unticked, but the service belongs" " to more than one workflow: this is incompatible.") return (valid_form and not conversion_validation_mismatch and not empty_validation and not forbidden_name_error and not no_recipient_error and not shared_service_error and not too_many_threads_error)