class MattermostNotificationService(Service): __tablename__ = "mattermost_notification_service" pretty_name = "Mattermost Notification" id = Column(Integer, ForeignKey("service.id"), primary_key=True) channel = Column(SmallString) body = Column(LargeString, default="") __mapper_args__ = { "polymorphic_identity": "mattermost_notification_service" } def job(self, run, payload, device=None): channel = (run.sub(run.channel, locals()) or app.settings["mattermost"]["channel"]) run.log("info", f"Sending MATTERMOST notification on {channel}", device) result = post( app.settings["mattermost"]["url"], verify=app.settings["mattermost"]["verify_certificate"], data=dumps({ "channel": channel, "text": run.sub(run.body, locals()) }), ) return {"success": True, "result": str(result)}
class MattermostNotificationService(Service): __tablename__ = "MattermostNotificationService" id = Column(Integer, ForeignKey("Service.id"), primary_key=True) has_targets = Column(Boolean, default=False) channel = Column(SmallString) body = Column(LargeString, default="") __mapper_args__ = {"polymorphic_identity": "MattermostNotificationService"} def job(self, run: "Run", payload: dict, device: Optional[Device] = None) -> dict: channel = run.sub(run.channel, locals()) or app.mattermost_channel run.log("info", f"Sending Mattermost notification on {channel}") result = post( app.mattermost_url, verify=app.mattermost_verify_certificate, data=dumps({ "channel": channel, "text": run.sub(run.body, locals()) }), ) return {"success": True, "result": str(result)}
class Result(AbstractBase): __tablename__ = type = "Result" private = True id = Column(Integer, primary_key=True) success = Column(Boolean, default=False) result = Column(MutableDict) run_id = Column(Integer, ForeignKey("Run.id")) run = relationship("Run", back_populates="results", foreign_keys="Result.run_id") device_id = Column(Integer, ForeignKey("Device.id")) device = relationship("Device", back_populates="results", foreign_keys="Result.device_id") device_name = association_proxy("device", "name") def __getitem__(self, key: Any) -> Any: return self.result[key] def __init__(self, **kwargs: Any) -> None: self.success = kwargs["result"]["success"] super().__init__(**kwargs) def __repr__(self) -> str: return f"{self.run} {self.device_name}"
class Syslog(Baselog): __tablename__ = "syslog" __mapper_args__ = {"polymorphic_identity": "syslog"} parent_type = "baselog" id = Column(Integer, ForeignKey("baselog.id"), primary_key=True) source = Column(LargeString, default="")
class NapalmTracerouteService(ConnectionService): __tablename__ = "napalm_traceroute_service" pretty_name = "NAPALM Traceroute" id = Column(Integer, ForeignKey("connection_service.id"), primary_key=True) driver = Column(SmallString) use_device_driver = Column(Boolean, default=True) timeout = Column(Integer, default=60) optional_args = Column(MutableDict) destination_ip = Column(SmallString) source_ip = Column(SmallString) timeout = Column(Integer, default=0) ttl = Column(Integer, default=0) vrf = Column(SmallString) __mapper_args__ = {"polymorphic_identity": "napalm_traceroute_service"} def job(self, run, payload, device): napalm_connection = run.napalm_connection(device) destination = run.sub(run.destination_ip, locals()) source = run.sub(run.source_ip, locals()) run.log("info", f"NAPALM TRACEROUTE : {source} -> {destination}", device) traceroute = napalm_connection.traceroute( destination=destination, source=run.source, vrf=run.vrf, ttl=run.ttl or 255, timeout=run.timeout or 2, ) return {"success": "success" in traceroute, "result": traceroute}
class Link(Object): __tablename__ = "link" __mapper_args__ = {"polymorphic_identity": "link"} class_type = "link" parent_type = "object" id = Column(Integer, ForeignKey("object.id"), primary_key=True) name = Column(SmallString) color = Column(SmallString, default="#000000") source_id = Column(Integer, ForeignKey("device.id")) destination_id = Column(Integer, ForeignKey("device.id")) source = relationship( Device, primaryjoin=source_id == Device.id, backref=backref("source", cascade="all, delete-orphan"), ) source_name = association_proxy("source", "name") destination = relationship( Device, primaryjoin=destination_id == Device.id, backref=backref("destination", cascade="all, delete-orphan"), ) destination_name = association_proxy("destination", "name") pools = relationship("Pool", secondary=pool_link_table, back_populates="links") __table_args__ = (UniqueConstraint(name, source_id, destination_id), ) def __init__(self, **kwargs): self.update(**kwargs) @property def view_properties(self): node_properties = ("id", "longitude", "latitude") return { **{ property: getattr(self, property) for property in ("id", "type", "name", "color") }, **{ f"source_{property}": getattr(self.source, property) for property in node_properties }, **{ f"destination_{property}": getattr(self.destination, property) for property in node_properties }, } def update(self, **kwargs): if "source_name" in kwargs: kwargs["source"] = fetch("device", name=kwargs.pop("source_name")).id kwargs["destination"] = fetch( "device", name=kwargs.pop("destination_name")).id kwargs.update({ "source_id": kwargs["source"], "destination_id": kwargs["destination"] }) super().update(**kwargs)
class Pool(AbstractPool): __tablename__ = "pool" parent_type = "abstract_pool" id = Column(Integer, ForeignKey("abstract_pool.id"), primary_key=True) name = Column(SmallString, unique=True) last_modified = Column(SmallString, info={"dont_track_changes": True}) description = Column(SmallString) operator = Column(SmallString, default="all") devices = relationship("Device", secondary=pool_device_table, back_populates="pools") device_number = Column(Integer, default=0) links = relationship("Link", secondary=pool_link_table, back_populates="pools") link_number = Column(Integer, default=0) latitude = Column(SmallString, default="0.0") longitude = Column(SmallString, default="0.0") services = relationship("Service", secondary=service_pool_table, back_populates="pools") runs = relationship("Run", secondary=run_pool_table, back_populates="pools") tasks = relationship("Task", secondary=task_pool_table, back_populates="pools") never_update = Column(Boolean, default=False) def update(self, **kwargs): super().update(**kwargs) self.compute_pool() def property_match(self, obj, property): pool_value = getattr(self, f"{obj.class_type}_{property}") object_value = str(getattr(obj, property)) match = getattr(self, f"{obj.class_type}_{property}_match") if not pool_value: return True elif match == "inclusion": return pool_value in object_value elif match == "equality": return pool_value == object_value else: return bool(search(pool_value, object_value)) def object_match(self, obj): operator = all if self.operator == "all" else any return operator( self.property_match(obj, property) for property in properties["filtering"][obj.class_type]) def compute_pool(self): if self.never_update: return self.devices = list(filter(self.object_match, fetch_all("device"))) self.device_number = len(self.devices) self.links = list(filter(self.object_match, fetch_all("link"))) self.link_number = len(self.links)
class PayloadValidationService(Service): __tablename__ = "PayloadValidationService" id = Column(Integer, ForeignKey("Service.id"), primary_key=True) has_targets = Column(Boolean, default=False) query = Column(SmallString) conversion_method = Column(SmallString, default="none") validation_method = Column(SmallString, default="text") content_match = Column(LargeString, default="") content_match_regex = Column(Boolean, default=False) dict_match = Column(MutableDict) negative_logic = Column(Boolean, default=False) delete_spaces_before_matching = Column(Boolean, default=False) __mapper_args__ = {"polymorphic_identity": "PayloadValidationService"} def job(self, run: "Run", payload: dict, device: Optional[Device] = None) -> dict: result = run.eval(run.query, **locals()) if self.conversion_method != "none": result = run.convert_result(result) match = (run.sub(run.content_match, locals()) if run.validation_method == "text" else run.sub(run.dict_match, locals())) return { "query": run.query, "match": match, "negative_logic": run.negative_logic, "result": result, "success": run.match_content(result, match), }
class User(AbstractBase, UserMixin): __tablename__ = type = "User" id = Column(Integer, primary_key=True) email = Column(SmallString) name = Column(SmallString) permissions = Column(MutableList) pools = relationship("Pool", secondary=pool_user_table, back_populates="users") password = Column(SmallString) def generate_row(self, table: str) -> List[str]: return [ f"""<button type="button" class="btn btn-primary btn-xs" onclick="showTypePanel('user', '{self.id}')">Edit</button>""", f"""<button type="button" class="btn btn-primary btn-xs" onclick="showTypePanel('user', '{self.id}', 'duplicate')"> Duplicate</button>""", f"""<button type="button" class="btn btn-danger btn-xs" onclick="showDeletionPanel('user', '{self.id}', '{self.name}')"> Delete</button>""", ] @property def is_admin(self) -> bool: return "Admin" in self.permissions def allowed(self, permission: str) -> bool: return self.is_admin or permission in self.permissions
class AnsiblePlaybookService(Service): __tablename__ = "ansible_playbook_service" pretty_name = "Ansible Playbook" id = Column(Integer, ForeignKey("service.id"), primary_key=True) playbook_path = Column(SmallString) arguments = Column(SmallString) options = Column(MutableDict) pass_device_properties = Column(Boolean, default=False) exit_codes = { "0": "OK or no hosts matched", "1": "Error", "2": "One or more hosts failed", "3": "One or more hosts were unreachable", "4": "Parser error", "5": "Bad or incomplete options", "99": "User interrupted execution", "250": "Unexpected error", } __mapper_args__ = {"polymorphic_identity": "ansible_playbook_service"} def job(self, run, payload, device=None): arguments = run.sub(run.arguments, locals()).split() command, extra_args = ["ansible-playbook"], {} if run.pass_device_properties: extra_args = device.get_properties() extra_args["password"] = device.password if run.options: extra_args.update(run.sub(run.options, locals())) if extra_args: command.extend(["-e", dumps(extra_args)]) if device: command.extend(["-i", device.ip_address + ","]) command.append(run.sub(run.playbook_path, locals())) password = extra_args.get("password") if password: safe_command = " ".join(command + arguments).replace( password, "*" * 10) run.log("info", f"Sending Ansible playbook: {safe_command}", device) try: result = check_output(command + arguments, cwd=app.path / "playbooks") except Exception: result = "\n".join(format_exc().splitlines()) if password: result = result.replace(password, "*" * 10) results = {"success": False, "result": result} exit_code = search(r"exit status (\d+)", result) if exit_code: results["exit_code"] = self.exit_codes[exit_code.group(1)] return results try: result = result.decode("utf-8") except AttributeError: pass return {"command": safe_command, "result": result}
class Changelog(Baselog): __tablename__ = "changelog" __mapper_args__ = {"polymorphic_identity": "changelog"} parent_type = "baselog" id = Column(Integer, ForeignKey("baselog.id"), primary_key=True) severity = Column(SmallString, default="N/A") user = Column(LargeString, default="")
class NapalmTracerouteService(Service): __tablename__ = "NapalmTracerouteService" id = Column(Integer, ForeignKey("Service.id"), primary_key=True) has_targets = True driver = Column(SmallString) use_device_driver = Column(Boolean, default=True) optional_args = Column(MutableDict) destination_ip = Column(SmallString) source_ip = Column(SmallString) timeout = Column(Integer, default=0) ttl = Column(Integer, default=0) vrf = Column(SmallString) __mapper_args__ = {"polymorphic_identity": "NapalmTracerouteService"} def job(self, run: "Run", payload: dict, device: Device) -> dict: napalm_connection = run.napalm_connection(device) destination = run.sub(run.destination_ip, locals()) source = run.sub(run.source_ip, locals()) run.log( "info", f"Running napalm traceroute from {source}" f"to {destination} on {device.ip_address}", ) traceroute = napalm_connection.traceroute( destination=destination, source=run.source, vrf=run.vrf, ttl=run.ttl or 255, timeout=run.timeout or 2, ) return {"success": "success" in traceroute, "result": traceroute}
class Object(AbstractBase): __tablename__ = "object" type = Column(SmallString) __mapper_args__ = { "polymorphic_identity": "object", "polymorphic_on": type } id = Column(Integer, primary_key=True) name = Column(SmallString, unique=True) last_modified = Column(SmallString, info={"dont_track_changes": True}) subtype = Column(SmallString) description = Column(SmallString) model = Column(SmallString) location = Column(SmallString) vendor = Column(SmallString) def update(self, **kwargs): super().update(**kwargs) if kwargs.get("dont_update_pools", False): return for pool in fetch_all("pool"): if pool.never_update: continue match = pool.object_match(self) relation, number = f"{self.class_type}s", f"{self.class_type}_number" if match and self not in pool.devices: getattr(pool, relation).append(self) setattr(pool, number, getattr(pool, number) + 1) if self in pool.devices and not match: getattr(pool, relation).remove(self) setattr(pool, number, getattr(pool, number) - 1)
class RestCallService(Service): __tablename__ = "rest_call_service" pretty_name = "REST Call" id = Column(Integer, ForeignKey("service.id"), primary_key=True) call_type = Column(SmallString) rest_url = Column(LargeString, default="") payload = Column(JSON, default={}) params = Column(JSON, default={}) headers = Column(JSON, default={}) verify_ssl_certificate = Column(Boolean, default=True) timeout = Column(Integer, default=15) username = Column(SmallString) password = Column(SmallString) request_dict = { "GET": rest_get, "POST": rest_post, "PUT": rest_put, "DELETE": rest_delete, "PATCH": rest_patch, } __mapper_args__ = {"polymorphic_identity": "rest_call_service"} def job(self, run, payload, device=None): rest_url = run.sub(run.rest_url, locals()) run.log("info", f"Sending REST Call to {rest_url}", device) kwargs = { p: run.sub(getattr(self, p), locals()) for p in ("headers", "params", "timeout") } kwargs["verify"] = run.verify_ssl_certificate if self.username: kwargs["auth"] = HTTPBasicAuth(self.username, self.password) if run.call_type in ("POST", "PUT", "PATCH"): kwargs["data"] = dumps(run.sub(run.payload, locals())) response = self.request_dict[run.call_type](rest_url, **kwargs) if response.status_code not in range(200, 300): result = { "success": False, "response_code": response.status_code, "response": response.text, } if response.status_code == 401: result["error"] = "Wrong credentials supplied." return result return { "url": rest_url, "status_code": response.status_code, "headers": dict(response.headers), "result": response.text, }
class PayloadValidationService(Service): __tablename__ = "payload_validation_service" pretty_name = "Payload Validation" id = Column(Integer, ForeignKey("service.id"), primary_key=True) query = Column(SmallString) __mapper_args__ = {"polymorphic_identity": "payload_validation_service"} def job(self, run, payload, device=None): return {"query": run.query, "result": run.eval(run.query, **locals())}
class NapalmBackupService(Service): __tablename__ = "NapalmBackupService" id = Column(Integer, ForeignKey("Service.id"), primary_key=True) configuration_backup_service = True has_targets = True number_of_configuration = Column(Integer, default=10) driver = Column(SmallString) use_device_driver = Column(Boolean, default=True) optional_args = Column(MutableDict) __mapper_args__ = {"polymorphic_identity": "NapalmBackupService"} def generate_yaml_file(self, path, device): data = { "last_failure": device.last_failure, "last_runtime": device.last_runtime, "last_update": device.last_update, "last_status": device.last_status, } with open(path / "data.yml", "w") as file: yaml.dump(data, file, default_flow_style=False) def job(self, run: "Run", payload: dict, device: Device) -> dict: try: now = datetime.now() path_configurations = Path.cwd() / "git" / "configurations" path_device_config = path_configurations / device.name path_device_config.mkdir(parents=True, exist_ok=True) napalm_connection = run.napalm_connection(device) run.log("info", f"Fetching configuration on {device.name} (Napalm)") config = controller.str_dict(napalm_connection.get_config()) device.last_status = "Success" device.last_runtime = (datetime.now() - now).total_seconds() if device.configurations: last_config = device.configurations[max(device.configurations)] if config == last_config: return {"success": True, "result": "no change"} device.configurations[str( now)] = device.current_configuration = config with open(path_device_config / device.name, "w") as file: file.write(config) device.last_update = str(now) self.generate_yaml_file(path_device_config, device) except Exception as e: device.last_status = "Failure" device.last_failure = str(now) self.generate_yaml_file(path_device_config, device) return {"success": False, "result": str(e)} if len(device.configurations) > self.number_of_configuration: device.configurations.pop(min(device.configurations)) return {"success": True, "result": "Get Config via Napalm"}
class Result(AbstractBase): __tablename__ = type = "result" private = True id = Column(Integer, primary_key=True) success = Column(Boolean, default=False) runtime = Column(SmallString) endtime = Column(SmallString) result = Column(MutableDict) run_id = Column(Integer, ForeignKey("run.id")) run = relationship("Run", back_populates="results", foreign_keys="Result.run_id") parent_runtime = Column(SmallString) device_id = Column(Integer, ForeignKey("device.id")) device = relationship("Device", back_populates="results", foreign_keys="Result.device_id") device_name = association_proxy("device", "name") service_id = Column(Integer, ForeignKey("service.id")) service = relationship("Service", foreign_keys="Result.service_id") service_name = association_proxy("service", "name") workflow_id = Column(Integer, ForeignKey("workflow.id")) workflow = relationship("Workflow", foreign_keys="Result.workflow_id") workflow_name = association_proxy("workflow", "name") def __repr__(self): return f"{self.service_name} on {self.device_name}" def __getitem__(self, key): return self.result[key] def __init__(self, **kwargs): self.success = kwargs["result"]["success"] self.runtime = kwargs["result"]["runtime"] self.endtime = kwargs["result"]["endtime"] super().__init__(**kwargs) self.parent_runtime = self.run.parent_runtime @property def table_success(self): btn = "success" if self.success else "danger" label = "Success" if self.success else "Failure" return (f'<button type="button" class="btn btn-{btn}"' f'style="width:100%">{label}</button>') def generate_row(self, table): return [ f"""<button type="button" class="btn btn-info btn-sm" onclick="showResult('{self.id}')">Results</button>""", f"""<input type="radio" name="v1" value="{self.id}"/>""", f"""<input type="radio" name="v2" value="{self.id}"/>""", ]
class NapalmGettersService(Service): __tablename__ = "NapalmGettersService" id = Column(Integer, ForeignKey("Service.id"), primary_key=True) has_targets = True validation_method = Column(SmallString, default="dict_included") dict_match = Column(MutableDict) driver = Column(SmallString) use_device_driver = Column(Boolean, default=True) getters = Column(MutableList) negative_logic = Column(Boolean, default=False) optional_args = Column(MutableDict) __mapper_args__ = {"polymorphic_identity": "NapalmGettersService"} def job(self, run: "Run", payload: dict, device: Device) -> dict: napalm_connection, result = run.napalm_connection(device), {} run.log( "info", f"Fetching NAPALM getters ({', '.join(run.getters)}) on {device.name}", ) for getter in run.getters: try: result[getter] = getattr(napalm_connection, getter)() except Exception as e: result[getter] = f"{getter} failed because of {e}" match = (run.sub(run.content_match, locals()) if run.validation_method == "text" else run.sub(run.dict_match, locals())) return { "match": match, "negative_logic": run.negative_logic, "result": result, "success": run.match_content(result, match), }
class UpdateInventoryService(Service): __tablename__ = "UpdateInventoryService" id = Column(Integer, ForeignKey("Service.id"), primary_key=True) has_targets = True update_dictionary = Column(MutableDict) __mapper_args__ = {"polymorphic_identity": "UpdateInventoryService"} def job(self, run: "Run", payload: dict, device: Device) -> dict: for property, value in run.update_dictionary.items(): setattr(device, property, value) return {"success": True, "result": "properties updated"}
class UpdateInventoryService(Service): __tablename__ = "update_inventory_service" pretty_name = "Update Inventory" id = Column(Integer, ForeignKey("service.id"), primary_key=True) update_dictionary = Column(MutableDict) __mapper_args__ = {"polymorphic_identity": "update_inventory_service"} def job(self, run, payload, device): for property, value in run.update_dictionary.items(): setattr(device, property, value) return {"success": True, "result": "properties updated"}
class GenericFileTransferService(Service): __tablename__ = "GenericFileTransferService" id = Column(Integer, ForeignKey("Service.id"), primary_key=True) has_targets = True direction = Column(SmallString) protocol = Column(SmallString) source_file = Column(SmallString) destination_file = Column(SmallString) missing_host_key_policy = Column(Boolean, default=False) load_known_host_keys = Column(Boolean, default=False) look_for_keys = Column(Boolean, default=False) source_file_includes_globbing = Column(Boolean, default=False) __mapper_args__ = {"polymorphic_identity": "GenericFileTransferService"} def job(self, run: "Run", payload: dict, device: Device) -> dict: ssh_client = SSHClient() if run.missing_host_key_policy: ssh_client.set_missing_host_key_policy(AutoAddPolicy()) if run.load_known_host_keys: ssh_client.load_system_host_keys() source = run.sub(run.source_file, locals()) destination = run.sub(run.destination_file, locals()) run.log("info", "Transferring file {source} on {device.name}") success, result = True, f"File {source} transferred successfully" ssh_client.connect( device.ip_address, username=device.username, password=device.password, look_for_keys=run.look_for_keys, ) if run.source_file_includes_globbing: glob_source_file_list = glob(source, recursive=False) if not glob_source_file_list: success = False result = f"Glob pattern {source} returned no matching files" else: pairs = [] for glob_source in glob_source_file_list: path, filename = split(glob_source) if destination[-1] != "/": destination = destination + "/" glob_destination = destination + filename pairs.append((glob_source, glob_destination)) info(f"Preparing to transfer glob file {glob_source}") run.transfer_file(ssh_client, pairs) else: run.transfer_file(ssh_client, [(source, destination)]) ssh_client.close() return {"success": success, "result": result}
class UnixCommandService(Service): __tablename__ = "unix_command_service" pretty_name = "Unix Command" id = Column(Integer, ForeignKey("service.id"), primary_key=True) command = Column(SmallString) __mapper_args__ = {"polymorphic_identity": "unix_command_service"} def job(self, run, payload, device): command = run.sub(run.command, locals()) run.log("info", f"Running UNIX command: {command}", device) return { "command": command, "result": check_output(command.split()).decode() }
class Object(AbstractBase): __tablename__ = "Object" type = Column(SmallString) __mapper_args__ = {"polymorphic_identity": "Object", "polymorphic_on": type} id = Column(Integer, primary_key=True) hidden = Column(Boolean, default=False) name = Column(SmallString, unique=True) subtype = Column(SmallString) description = Column(SmallString) model = Column(SmallString) location = Column(SmallString) vendor = Column(SmallString)
class NapalmRollbackService(Service): __tablename__ = "NapalmRollbackService" id = Column(Integer, ForeignKey("Service.id"), primary_key=True) has_targets = True driver = Column(SmallString) use_device_driver = Column(Boolean, default=True) optional_args = Column(MutableDict) __mapper_args__ = {"polymorphic_identity": "NapalmRollbackService"} def job(self, run: "Run", payload: dict, device: Device) -> dict: napalm_connection = run.napalm_connection(device) run.log("info", f"Configuration rollback on {device.name} (Napalm)") napalm_connection.rollback() return {"success": True, "result": "Rollback successful"}
class WorkflowEdge(AbstractBase): __tablename__ = type = "workflow_edge" id = Column(Integer, primary_key=True) name = Column(SmallString) label = Column(SmallString) subtype = Column(SmallString) source_id = Column(Integer, ForeignKey("service.id")) source = relationship( "Service", primaryjoin="Service.id == WorkflowEdge.source_id", backref=backref("destinations", cascade="all, delete-orphan"), foreign_keys="WorkflowEdge.source_id", ) destination_id = Column(Integer, ForeignKey("service.id")) destination = relationship( "Service", primaryjoin="Service.id == WorkflowEdge.destination_id", backref=backref("sources", cascade="all, delete-orphan"), foreign_keys="WorkflowEdge.destination_id", ) workflow_id = Column(Integer, ForeignKey("workflow.id")) workflow = relationship("Workflow", back_populates="edges", foreign_keys="WorkflowEdge.workflow_id") def __init__(self, **kwargs): self.label = kwargs["subtype"] super().__init__(**kwargs)
class PingService(Service): __tablename__ = "ping_service" pretty_name = "ICMP / TCP Ping" id = Column(Integer, ForeignKey("service.id"), primary_key=True) protocol = Column(SmallString) ports = Column(SmallString) count = Column(Integer, default=5) timeout = Column(Integer, default=2) ttl = Column(Integer, default=60) packet_size = Column(Integer, default=56) __mapper_args__ = {"polymorphic_identity": "ping_service"} def job(self, run, payload, device): if run.protocol == "ICMP": command = ["ping"] for x, property in ( ("c", "count"), ("W", "timeout"), ("t", "ttl"), ("s", "packet_size"), ): value = getattr(self, property) if value: command.extend(f"-{x} {value}".split()) command.append(device.ip_address) run.log("info", f"Running PING ({command})", device) try: output = check_output(command).decode().strip().splitlines() except CalledProcessError: return {"success": False, "error": "Device not pingable"} total = output[-2].split(",")[3].split()[1] loss = output[-2].split(",")[2].split()[0] timing = output[-1].split()[3].split("/") return { "success": True, "result": { "probes_sent": run.count, "packet_loss": loss, "rtt_min": timing[0], "rtt_max": timing[2], "rtt_avg": timing[1], "rtt_stddev": timing[3], "total rtt": total, }, } else: result = {} for port in map(int, run.ports.split(",")): s = socket() s.settimeout(run.timeout) try: connection = not s.connect_ex((device.ip_address, port)) except (gaierror, timeout, error): connection = False finally: s.close() result[port] = connection return {"success": all(result.values()), "result": result}
class NapalmBackupService(ConnectionService): __tablename__ = "napalm_backup_service" pretty_name = "NAPALM Operational Data Backup" parent_type = "connection_service" id = Column(Integer, ForeignKey("connection_service.id"), primary_key=True) driver = Column(SmallString) use_device_driver = Column(Boolean, default=True) timeout = Column(Integer, default=60) optional_args = Column(MutableDict) configuration_getters = Column(MutableList) operational_data_getters = Column(MutableList) replacements = Column(MutableList) __mapper_args__ = {"polymorphic_identity": "napalm_backup_service"} def job(self, run, payload, device): path = Path.cwd() / "network_data" / device.name path.mkdir(parents=True, exist_ok=True) try: device.last_runtime = datetime.now() napalm_connection = run.napalm_connection(device) run.log("info", "Fetching Operational Data", device) for data_type in ("configuration_getters", "operational_data_getters"): result = {} for getter in getattr(run, data_type): try: output = app.str_dict( getattr(napalm_connection, getter)()) for r in self.replacements: output = sub( r["pattern"], r["replace_with"], output, flags=M, ) result[getter] = output except Exception as e: result[getter] = f"{getter} failed because of {e}" result = app.str_dict(result) setattr(device, data_type, result) with open(path / data_type, "w") as file: file.write(result) device.last_status = "Success" device.last_duration = ( f"{(datetime.now() - device.last_runtime).total_seconds()}s") device.last_update = str(device.last_runtime) run.generate_yaml_file(path, device) except Exception as e: device.last_status = "Failure" device.last_failure = str(device.last_runtime) run.generate_yaml_file(path, device) return {"success": False, "result": str(e)} return {"success": True}
class User(AbstractBase, UserMixin): __tablename__ = type = "user" id = Column(Integer, primary_key=True) name = Column(SmallString, unique=True) email = Column(SmallString) permissions = Column(MutableList) password = Column(SmallString) group = Column(SmallString) small_menu = Column(Boolean, default=False, info={"dont_track_changes": True}) def update(self, **kwargs): if app.settings["security"][ "hash_user_passwords"] and "password" in kwargs: kwargs["password"] = argon2.hash(kwargs["password"]) super().update(**kwargs) @property def is_admin(self): return "Admin" in self.permissions def allowed(self, permission): return self.is_admin or permission in self.permissions
class NapalmRollbackService(ConnectionService): __tablename__ = "napalm_rollback_service" pretty_name = "NAPALM Rollback" parent_type = "connection_service" id = Column(Integer, ForeignKey("connection_service.id"), primary_key=True) driver = Column(SmallString) use_device_driver = Column(Boolean, default=True) timeout = Column(Integer, default=60) optional_args = Column(MutableDict) __mapper_args__ = {"polymorphic_identity": "napalm_rollback_service"} def job(self, run, payload, device): napalm_connection = run.napalm_connection(device) run.log("info", "Configuration Rollback with NAPALM", device) napalm_connection.rollback() return {"success": True, "result": "Rollback successful"}
class Baselog(AbstractBase): __tablename__ = "baselog" type = Column(SmallString) __mapper_args__ = { "polymorphic_identity": "baselog", "polymorphic_on": type } id = Column(Integer, primary_key=True) time = Column(SmallString) content = Column(LargeString, default="") def update(self, **kwargs): kwargs["time"] = str(datetime.now()) super().update(**kwargs) def generate_row(self, table): return []