class NapalmForm(BaseForm): form_type = HiddenField(default="napalm") abstract_service = True driver = SelectField(choices=controller.NAPALM_DRIVERS) use_device_driver = BooleanField(default=True) optional_args = DictField() group = ["driver", "use_device_driver", "optional_args"]
class RestCallForm(ServiceForm, ValidationForm): form_type = HiddenField(default="RestCallService") has_targets = BooleanField("Has Target Devices") call_type = SelectField(choices=(("GET", "GET"), ("POST", "POST"), ("PUT", "PUT"), ("DELETE", "DELETE"))) rest_url = SubstitutionField() payload = DictSubstitutionField() params = DictSubstitutionField() headers = DictSubstitutionField() verify_ssl_certificate = BooleanField("Verify SSL Certificate") timeout = IntegerField(default=15) username = StringField() password = StringField() pass_device_properties = BooleanField() options = DictField() groups = { "Main Parameters": [ "call_type", "rest_url", "payload", "params", "headers", "verify_ssl_certificate", "timeout", "username", "password", "pass_device_properties", "options", ], "Validation Parameters": ValidationForm.group, }
class TaskForm(BaseForm): template = "object" form_type = HiddenField(default="task") id = HiddenField() is_active = BooleanField("Is Active") name = StringField("Name") description = StringField("Description") start_date = DateField("Start Date") end_date = DateField("End 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") job = InstanceField("Job", instance_type="Job") scheduling_mode = SelectField( "Scheduling Mode", choices=(("standard", "Standard Scheduling"), ("cron", "Crontab Scheduling")), ) devices = MultipleInstanceField("Devices", instance_type="Device") pools = MultipleInstanceField("Pools", instance_type="Pool") payload = DictField("Payload")
class RestCallForm(ServiceForm): form_type = HiddenField(default="rest_call_service") call_type = SelectField(choices=( ("GET", "GET"), ("POST", "POST"), ("PUT", "PUT"), ("DELETE", "DELETE"), ("PATCH", "PATCH"), )) rest_url = StringField(substitution=True) payload = DictField(json_only=True, substitution=True) params = DictField(substitution=True) headers = DictField(substitution=True) verify_ssl_certificate = BooleanField("Verify SSL Certificate") timeout = IntegerField(default=15) username = StringField() password = PasswordField()
class NapalmForm(BaseForm): form_type = HiddenField(default="napalm") abstract_service = True driver = SelectField(choices=app.NAPALM_DRIVERS) use_device_driver = BooleanField(default=True) optional_args = DictField() group = { "commands": ["driver", "use_device_driver", "optional_args"], "default": "expanded", }
class AnsiblePlaybookForm(ServiceForm): form_type = HiddenField(default="ansible_playbook_service") playbook_path = SelectField("Playbook Path", choices=(), validation=False) arguments = StringField("Arguments (Ansible command line options)", substitution=True) pass_device_properties = BooleanField( "Pass Device Inventory Properties (to be used " "in the playbook as {{name}} or {{ip_address}})") options = DictField("Options (passed to ansible as -e extra args)", substitution=True)
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") @classmethod def form_init(cls): cls.configure_relationships("devices", "pools", "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 NapalmForm(ConnectionForm): form_type = HiddenField(default="napalm") abstract_service = True driver = SelectField(choices=app.NAPALM_DRIVERS) use_device_driver = BooleanField(default=True) timeout = IntegerField(default=10) optional_args = DictField() groups = { "Napalm Parameters": { "commands": ["driver", "use_device_driver", "timeout", "optional_args"], "default": "expanded", }, **ConnectionForm.groups, }
class IterationForm(ServiceForm): form_type = HiddenField(default="IterationService") has_targets = BooleanField("Has Target Devices") origin_of_values = SelectField( "Where Values come from", choices=( ("user_provided_values", "User-provided Values (dictionary)"), ("yaql_query", "YaQL Query on the Payload"), ), ) user_provided_values = DictField( "Iteration Values for Iteration: User provided " "(Expect dictionary {'all' : [...]} or {'device-name' : [...], ...})" ) yaql_query_values = SubstitutionField( "Iteration Values for Iteration: YaQL query on the payload" ) variable_name = StringField("Iteration Variable Name") iterated_job = InstanceField("Job to run for each Value", instance_type="Job")
class AnsiblePlaybookForm(ServiceForm, ValidationForm): form_type = HiddenField(default="AnsiblePlaybookService") has_targets = BooleanField("Has Target Devices") playbook_path = NoValidationSelectField("Playbook Path", choices=()) arguments = SubstitutionField("Arguments (Ansible command line options)") pass_device_properties = BooleanField( "Pass Device Inventory Properties (to be used " "in the playbook as {{name}} or {{ip_address}})") options = DictField("Options (passed to ansible as -e extra args)") groups = { "Main Parameters": [ "playbook_path", "arguments", "pass_device_properties", "options", ], "Validation Parameters": ValidationForm.group, }
class TaskForm(BaseForm): template = "object" form_type = HiddenField(default="task") id = HiddenField() scheduling_mode = SelectField( "Scheduling Mode", choices=(("cron", "Crontab Scheduling"), ("standard", "Standard Scheduling")), ) name = StringField("Name", [InputRequired()]) description = StringField("Description") start_date = DateField("Start Date") end_date = DateField("End 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") def validate(self): valid_form = super().validate() 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 ServiceForm(BaseForm): template = "service" form_type = HiddenField(default="service") id = HiddenField() name = StringField("Name") type = StringField("Service Type") shared = BooleanField("Shared Service") scoped_name = StringField("Scoped Name", [InputRequired()]) description = StringField("Description") device_query = StringField("Device Query") device_query_property = SelectField("Query Property Type", choices=(("name", "Name"), ("ip_address", "IP address"))) devices = MultipleInstanceField("Devices") pools = MultipleInstanceField("Pools") workflows = MultipleInstanceField("Workflows") waiting_time = IntegerField("Waiting time (in seconds)", default=0) 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}) 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)") number_of_retries = IntegerField("Number of retries", default=0) time_between_retries = IntegerField("Time between retries (in seconds)", default=10) maximum_runs = IntegerField("Maximum number of runs", default=1) skip = BooleanField("Skip") skip_query = StringField("Skip Query (Python)") vendor = StringField("Vendor") operating_system = StringField("Operating System") initial_payload = DictField() iteration_values = StringField("Iteration Values (Python Query)") iteration_variable_name = StringField("Iteration Variable Name", default="iteration_value") iteration_devices = StringField("Iteration Devices (Python Query)") iteration_devices_property = SelectField( "Iteration Devices Property", choices=(("name", "Name"), ("ip_address", "IP address")), ) result_postprocessing = StringField(widget=TextArea(), render_kw={"rows": 7}) multiprocessing = BooleanField("Multiprocessing") max_processes = IntegerField("Maximum number of processes", default=50) conversion_method = SelectField(choices=( ("none", "No conversion"), ("text", "Text"), ("json", "Json dictionary"), ("xml", "XML dictionary"), )) validation_method = SelectField( "Validation Method", choices=( ("none", "No validation"), ("text", "Validation by text match"), ("dict_included", "Validation by dictionary inclusion"), ("dict_equal", "Validation by dictionary equality"), ), ) content_match = SubstitutionField("Content Match", widget=TextArea(), render_kw={"rows": 8}) content_match_regex = BooleanField("Match content with Regular Expression") dict_match = DictSubstitutionField("Dictionary to Match Against") negative_logic = BooleanField("Negative logic") delete_spaces_before_matching = BooleanField( "Delete Spaces before Matching") query_fields = [ "device_query", "skip_query", "iteration_values", "result_postprocessing", ] 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 and not app.config["mail"]["recipients"]) if no_recipient_error: self.mail_recipient.errors.append( "Please add at least one recipient for the mail notification.") bracket_error = False for query_field in self.query_fields: field = getattr(self, query_field) try: parse(field.data) except Exception as exc: bracket_error = True field.errors.append(f"Wrong python expression ({exc}).") if "{{" in field.data and "}}" in field.data: bracket_error = True field.errors.append( "You cannot use variable substitution " "in a field expecting a python expression.") conversion_validation_mismatch = ( 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.") return (valid_form and not no_recipient_error and not bracket_error and not conversion_validation_mismatch)
class JobForm(BaseForm): template = "job" form_type = HiddenField(default="job") id = HiddenField() type = StringField("Service Type") name = StringField("Name") description = StringField("Description") python_query = StringField("Python Query") query_property_type = SelectField("Query Property Type", choices=(("name", "Name"), ("ip_address", "IP address"))) devices = MultipleInstanceField("Devices", instance_type="Device") pools = MultipleInstanceField("Pools", instance_type="Pool") waiting_time = IntegerField("Waiting time (in seconds)", default=0) send_notification = BooleanField("Send a notification") send_notification_method = SelectField( "Notification Method", choices=( ("mail_feedback_notification", "Mail"), ("slack_feedback_notification", "Slack"), ("mattermost_feedback_notification", "Mattermost"), ), ) notification_header = StringField(widget=TextArea(), render_kw={"rows": 5}) 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)") number_of_retries = IntegerField("Number of retries", default=0) time_between_retries = IntegerField("Time between retries (in seconds)", default=10) start_new_connection = BooleanField("Start New Connection") skip = BooleanField("Skip") skip_python_query = StringField("Skip (Python Query)") vendor = StringField("Vendor") operating_system = StringField("Operating System") shape = SelectField( "Shape", choices=( ("box", "Box"), ("circle", "Circle"), ("square", "Square"), ("diamond", "Diamond"), ("triangle", "Triangle"), ("ellipse", "Ellipse"), ("database", "Database"), ), ) size = IntegerField("Size", default=40) color = StringField("Color", default="#D2E5FF") initial_payload = DictField() iteration_targets = StringField("Iteration Targets (Python Query)") query_fields = ["python_query", "skip_python_query", "iteration_targets"] def validate(self) -> bool: valid_form = super().validate() no_recipient_error = (self.send_notification.data and self.send_notification_method.data == "mail_feedback_notification" and not self.mail_recipient.data and not controller.mail_recipients) bracket_error = False for query_field in self.query_fields: field = getattr(self, query_field) try: parse(field.data) except Exception as exc: bracket_error = True field.errors.append(f"Wrong python expression ({exc}).") if "{{" in field.data and "}}" in field.data: bracket_error = True field.errors.append( "You cannot use variable substitution " "in a field expecting a python expression.") if no_recipient_error: self.mail_recipient.errors.append( "Please add at least one recipient for the mail notification.") return valid_form and not no_recipient_error and not bracket_error
class ServiceForm(BaseForm): template = "service" form_type = HiddenField(default="service") id = HiddenField() name = StringField("Name") type = StringField("Service Type") shared = BooleanField("Shared Service") 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")) ) devices = MultipleInstanceField("Devices") disable_result_creation = BooleanField("Disable Result Creation") pools = MultipleInstanceField("Pools") update_pools = BooleanField("Update pools before running") workflows = MultipleInstanceField("Workflows") waiting_time = IntegerField( "Time to Wait before next service is started (in seconds)", default=0 ) 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}) 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) maximum_runs = IntegerField("Maximum number of runs", default=1) skip = BooleanField("Skip") skip_query = StringField( "Skip Query (Python)", python=True, widget=TextArea(), render_kw={"rows": 2} ) skip_value = SelectField( "Skip Value", choices=(("True", "True"), ("False", "False")), ) vendor = StringField("Vendor") operating_system = StringField("Operating System") iteration_values = StringField("Iteration Values", python=True) initial_payload = DictField() 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=( ("always", "Always run"), ("success", "Run on success only"), ("failure", "Run on failure only"), ) ) public = BooleanField("Public") log_level = SelectField( "Logging", choices=((0, "Disable logging"), *enumerate(app.log_levels, 1)), default=1, validation=False, ) multiprocessing = BooleanField("Multiprocessing") max_processes = IntegerField("Maximum number of processes", default=15) validation_condition = SelectField( choices=( ("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=( ("none", "No validation"), ("text", "Validation by text match"), ("dict_included", "Validation by dictionary inclusion"), ("dict_equal", "Validation by dictionary equality"), ), ) content_match = StringField( "Content Match", widget=TextArea(), render_kw={"rows": 8}, substitution=True ) content_match_regex = BooleanField("Match content with 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.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." ) return ( valid_form and not no_recipient_error and not conversion_validation_mismatch and not forbidden_name_error )
class UpdateInventoryForm(ServiceForm): form_type = HiddenField(default="update_inventory_service") update_dictionary = DictField()
class ExampleForm(ServiceForm): # Each service model must have an corresponding form. # The purpose of a form is twofold: # - Define how the service is displayed in the UI # - Check for each field that the user input is valid. # A service cannot be created/updated until all fields are validated. # The following line is mandatory: the default value must point # to the service. form_type = HiddenField(default="ExampleService") # string1 is defined as a "SelectField": it will be displayed as a # drop-down list in the UI. string1 = SelectField(choices=[("cisco", "Cisco"), ("juniper", "Juniper"), ("arista", "Arista")]) # String2 is a StringField, which is displayed as a standard textbox. # The "InputRequired" validator is used: this field is mandatory. string2 = StringField("String 2 (required)", [InputRequired()]) # The main address field uses two validators: # - The input length must be comprised between 7 and 25 characters # - The input syntax must match that of an email address. mail_address = StringField( "Mail address", [Length(min=7, max=25), Email()]) # This IP address validator will ensure the user input is a valid IPv4 address. # If it isn't, you can set the error message to be displayed in the GUI. ip_address = StringField( "IP address", [ IPAddress( ipv4=True, message="Please enter an IPv4 address for the IP address field", ) ], ) # MAC address validator mac_address = StringField("MAC address", [MacAddress()]) # The NumberRange validator will ensure the user input is an integer # between 3 and 8. number_in_range = IntegerField("Number in range", [NumberRange(min=3, max=8)]) # The Regexp field will ensure the user input matches the regular expression. regex = StringField("Regular expression", [Regexp(r".*")]) # URL validation, with or without TLD. url = StringField( "URL", [ URL( require_tld=True, message="An URL with TLD is required for the url field", ) ], ) # The NoneOf validator lets you define forbidden value for a field. exclusion_field = StringField( "Exclusion field", [ NoneOf( ("a", "b", "c"), message=("'a', 'b', and 'c' are not valid " "inputs for the exclusion field"), ) ], ) an_integer = IntegerField() a_float = FloatField() # If validator the user input is more complex, you can create a python function # to implement the validation mechanism. # Here, the custom_integer field will be validated by the "validate_custom_integer" # function below. # That function will check that the custom integer value is superior to the product # of "an_integer" and "a_float". # You must raise a "ValidationError" when the validation fails. custom_integer = IntegerField("Custom Integer") # A SelectMultipleField will be displayed as a drop-down list that allows # multiple selection. a_list = SelectMultipleField( choices=[("value1", "Value 1"), ("value2", "Value 2"), ("value3", "Value 3")]) a_dict = DictField() # A BooleanField is displayed as a check box. boolean1 = BooleanField() boolean2 = BooleanField("Boolean N°1") def validate_custom_integer(self, field: IntegerField) -> None: product = self.an_integer.data * self.a_float.data if field.data > product: raise ValidationError("Custom integer must be less than the " "product of 'An integer' and 'A float'")