class NapalmTracerouteService(ConnectionService): __tablename__ = "napalm_traceroute_service" pretty_name = "NAPALM Traceroute" parent_type = "connection_service" id = db.Column(Integer, ForeignKey("connection_service.id"), primary_key=True) driver = db.Column(db.SmallString) use_device_driver = db.Column(Boolean, default=True) timeout = db.Column(Integer, default=60) optional_args = db.Column(db.Dict) destination_ip = db.Column(db.SmallString) source_ip = db.Column(db.SmallString) timeout = db.Column(Integer, default=0) ttl = db.Column(Integer, default=0) vrf = db.Column(db.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 Access(AbstractBase): __tablename__ = type = class_type = "access" id = db.Column(Integer, primary_key=True) name = db.Column(db.SmallString, unique=True) description = db.Column(db.LargeString) menu = db.Column(db.List) pages = db.Column(db.List) upper_menu = db.Column(db.List) get_requests = db.Column(db.List) post_requests = db.Column(db.List) delete_requests = db.Column(db.List) user_pools = relationship("Pool", secondary=db.access_user_pools_table, back_populates="access_users") access_pools = relationship("Pool", secondary=db.access_model_pools_table, back_populates="access") access_type = db.Column(db.SmallString) def get_users(self): return set(chain.from_iterable(pool.users for pool in self.user_pools)) def update(self, **kwargs): old_users = self.get_users() super().update(**kwargs) for user in old_users | self.get_users(): user.update_rbac()
class Object(AbstractBase): __tablename__ = "object" pool_model = True type = db.Column(db.SmallString) __mapper_args__ = { "polymorphic_identity": "object", "polymorphic_on": type } id = db.Column(Integer, primary_key=True) creator = db.Column(db.SmallString) access_groups = db.Column(db.LargeString) last_modified = db.Column(db.TinyString, info={"log_change": False}) subtype = db.Column(db.SmallString) description = db.Column(db.LargeString) model = db.Column(db.SmallString) location = db.Column(db.SmallString) vendor = db.Column(db.SmallString) def delete(self): number = f"{self.class_type}_number" for pool in self.pools: setattr(pool, number, getattr(pool, number) - 1) @classmethod def rbac_filter(cls, query, mode, user): pool_alias = aliased(models["pool"]) return (query.join(cls.pools).join( models["access"], models["pool"].access).join( pool_alias, models["access"].user_pools).join( models["user"], pool_alias.users).filter( models["access"].access_type.contains(mode)).filter( models["user"].name == user.name).group_by(cls.id))
class MattermostNotificationService(Service): __tablename__ = "mattermost_notification_service" pretty_name = "Mattermost Notification" id = db.Column(Integer, ForeignKey("service.id"), primary_key=True) channel = db.Column(db.SmallString) body = db.Column(db.LargeString) __mapper_args__ = { "polymorphic_identity": "mattermost_notification_service" } def job(self, run, device=None): channel = run.sub(run.channel, locals()) or vs.settings["mattermost"]["channel"] run.log("info", f"Sending MATTERMOST notification on {channel}", device) result = post( vs.settings["mattermost"]["url"], verify=vs.settings["mattermost"]["verify_certificate"], json={ "channel": channel, "text": run.sub(run.body, locals()) }, ) return {"success": True, "result": str(result)}
class Link(Object): __tablename__ = class_type = "link" __mapper_args__ = {"polymorphic_identity": "link"} parent_type = "object" id = db.Column(Integer, ForeignKey("object.id"), primary_key=True) name = db.Column(db.SmallString) color = db.Column(db.TinyString, default="#000000") source_id = db.Column(Integer, ForeignKey("device.id")) destination_id = db.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=db.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, None) for property in node_properties }, **{ f"destination_{property}": getattr(self.destination, property, None) for property in node_properties }, } def update(self, **kwargs): if "source_name" in kwargs: kwargs["source"] = db.fetch("device", name=kwargs.pop("source_name")).id kwargs["destination"] = db.fetch( "device", name=kwargs.pop("destination_name")).id if "source" in kwargs and "destination" in kwargs: kwargs.update({ "source_id": kwargs["source"], "destination_id": kwargs["destination"] }) super().update(**kwargs)
class Access(AbstractBase): __tablename__ = type = "access" id = db.Column(Integer, primary_key=True) name = db.Column(db.SmallString, unique=True) description = db.Column(db.SmallString) menu = db.Column(db.List) pages = db.Column(db.List) upper_menu = db.Column(db.List) get_requests = db.Column(db.List) post_requests = db.Column(db.List) users = relationship("User", secondary=db.access_user_table, back_populates="access") groups = relationship("Group", secondary=db.access_group_table, back_populates="access") pools = relationship("Pool", secondary=db.access_pool_table, back_populates="access") services = relationship("Service", secondary=db.access_service_table, back_populates="access") services_access = db.Column(db.SmallString) pools_access = db.Column(db.SmallString) def get_users(self): group_users = chain.from_iterable(group.users for group in self.groups) return set(self.users) | set(group_users) def update(self, **kwargs): old_users = self.get_users() super().update(**kwargs) for user in old_users | self.get_users(): user.update_rbac()
class Label(ViewObject): __tablename__ = class_type = "label" __mapper_args__ = {"polymorphic_identity": "label"} parent_type = "view_object" id = db.Column(Integer, ForeignKey(ViewObject.id), primary_key=True) text = db.Column(db.SmallString)
class Pool(AbstractBase): __tablename__ = type = "pool" id = db.Column(Integer, primary_key=True) name = db.Column(db.SmallString, unique=True) last_modified = db.Column(db.SmallString, info={"dont_track_changes": True}) description = db.Column(db.SmallString) operator = db.Column(db.SmallString, default="all") devices = relationship("Device", secondary=db.pool_device_table, back_populates="pools") device_number = db.Column(Integer, default=0) links = relationship("Link", secondary=db.pool_link_table, back_populates="pools") link_number = db.Column(Integer, default=0) latitude = db.Column(db.SmallString, default="0.0") longitude = db.Column(db.SmallString, default="0.0") services = relationship("Service", secondary=db.service_pool_table, back_populates="pools") runs = relationship("Run", secondary=db.run_pool_table, back_populates="pools") tasks = relationship("Task", secondary=db.task_pool_table, back_populates="pools") manually_defined = db.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.manually_defined: return self.devices = list(filter(self.object_match, db.fetch_all("device"))) self.device_number = len(self.devices) self.links = list(filter(self.object_match, db.fetch_all("link"))) self.link_number = len(self.links)
class View(ViewObject): __tablename__ = class_type = "view" __mapper_args__ = {"polymorphic_identity": "view"} parent_type = "view_object" id = db.Column(Integer, ForeignKey(ViewObject.id), primary_key=True) last_modified = db.Column(db.TinyString, info={"log_change": False}) objects = relationship("ViewObject", foreign_keys="ViewObject.view_id")
class PythonSnippetService(Service): __tablename__ = "python_snippet_service" pretty_name = "Python Snippet" id = db.Column(Integer, ForeignKey("service.id"), primary_key=True) source_code = db.Column(db.LargeString) __mapper_args__ = {"polymorphic_identity": "python_snippet_service"} def job(self, run, device=None): try: code_object = compile(run.source_code, "user_python_code", "exec") except Exception as exc: run.log("info", f"Compile error: {str(exc)}") return {"success": False, "result": {"step": "compile", "error": str(exc)}} results = {} def save_result(success, result, **kwargs): results.update({"success": success, "result": result, **kwargs}) if kwargs.get("exit"): raise SystemExit() globals = { "results": results, "save_result": save_result, **run.global_variables(**locals()), } try: exec(code_object, globals) except SystemExit: pass except Exception as exc: line_number = extract_tb(exc.__traceback__)[-1][1] run.log("info", f"Execution error(line {line_number}): {str(exc)}") return { "success": False, "result": { "step": "execute", "error": str(exc), "result": results, "traceback": format_exc(), }, } if not results: run.log("info", "Error: Result not set by user code on service instance") results = { "success": False, "result": {"error": "Result not set by user code on service instance"}, } return results
class DataValidationService(Service): __tablename__ = "data_validation_service" pretty_name = "Data Validation" id = db.Column(Integer, ForeignKey("service.id"), primary_key=True) query = db.Column(db.SmallString) __mapper_args__ = {"polymorphic_identity": "data_validation_service"} def job(self, run, payload, device=None): return {"query": run.query, "result": run.eval(run.query, **locals())[0]}
class UnixCommandService(Service): __tablename__ = "unix_command_service" pretty_name = "Unix Command" id = db.Column(Integer, ForeignKey("service.id"), primary_key=True) command = db.Column(db.SmallString) __mapper_args__ = {"polymorphic_identity": "unix_command_service"} def job(self, run, device=None): 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 ServiceLog(AbstractBase): __tablename__ = type = "service_log" private = True log_change = False id = db.Column(Integer, primary_key=True) content = db.Column(db.LargeString) runtime = db.Column(db.TinyString) service_id = db.Column(Integer, ForeignKey("service.id")) service = relationship("Service", foreign_keys="ServiceLog.service_id") def __repr__(self): return f"SERVICE '{self.service}' ({self.runtime})"
class Line(ViewObject): __tablename__ = class_type = "line" __mapper_args__ = {"polymorphic_identity": "line"} parent_type = "view_object" id = db.Column(Integer, ForeignKey(ViewObject.id), primary_key=True) link_id = db.Column(Integer, ForeignKey("link.id")) link = relationship("Link", foreign_keys="Line.link_id") link_name = association_proxy("link", "name") def __init__(self, **kwargs): super().__init__(**kwargs) if not self.name: self.name = vs.get_time()
class Object(AbstractBase): __tablename__ = "object" type = db.Column(db.SmallString) __mapper_args__ = {"polymorphic_identity": "object", "polymorphic_on": type} id = db.Column(Integer, primary_key=True) last_modified = db.Column(db.SmallString, info={"dont_track_changes": True}) subtype = db.Column(db.SmallString) description = db.Column(db.SmallString) model = db.Column(db.SmallString) location = db.Column(db.SmallString) vendor = db.Column(db.SmallString) def update(self, **kwargs): super().update(**kwargs) if kwargs.get("dont_update_pools", False): return for pool in db.fetch_all("pool"): if pool.manually_defined: continue match = pool.object_match(self) relation, number = f"{self.class_type}s", f"{self.class_type}_number" if match and self not in getattr(pool, relation): getattr(pool, relation).append(self) setattr(pool, number, getattr(pool, number) + 1) if self in getattr(pool, relation) and not match: getattr(pool, relation).remove(self) setattr(pool, number, getattr(pool, number) - 1) def delete(self): number = f"{self.class_type}_number" for pool in self.pools: setattr(pool, number, getattr(pool, number) - 1)
class Group(AbstractBase): __tablename__ = type = "group" id = db.Column(Integer, primary_key=True) name = db.Column(db.SmallString, unique=True) email = db.Column(db.SmallString) menu = db.Column(db.List) pages = db.Column(db.List) upper_menu = db.Column(db.List) get_requests = db.Column(db.List) post_requests = db.Column(db.List) users = relationship("User", secondary=db.user_group_table, back_populates="groups") pools = relationship("Pool", secondary=db.pool_group_table, back_populates="groups") services = relationship("Service", secondary=db.service_group_table, back_populates="groups") def update(self, **kwargs): super().update(**kwargs) self.update_rbac() def update_rbac(self): for user in self.users: user.update_rbac()
class NapalmBackupService(ConnectionService): __tablename__ = "napalm_backup_service" pretty_name = "NAPALM Operational Data Backup" parent_type = "connection_service" id = db.Column(Integer, ForeignKey("connection_service.id"), primary_key=True) driver = db.Column(db.SmallString) use_device_driver = db.Column(Boolean, default=True) timeout = db.Column(Integer, default=60) optional_args = db.Column(db.Dict) configuration_getters = db.Column(db.List) operational_data_getters = db.Column(db.List) replacements = db.Column(db.List) __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 exc: result[getter] = f"{getter} failed because of {exc}" if not result: continue 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 exc: device.last_status = "Failure" device.last_failure = str(device.last_runtime) run.generate_yaml_file(path, device) return {"success": False, "result": str(exc)} return {"success": True}
class RestCallService(Service): __tablename__ = "rest_call_service" pretty_name = "REST Call" id = db.Column(Integer, ForeignKey("service.id"), primary_key=True) call_type = db.Column(db.SmallString) rest_url = db.Column(db.LargeString) payload = db.Column(JSON, default={}) params = db.Column(JSON, default={}) headers = db.Column(JSON, default={}) verify_ssl_certificate = db.Column(Boolean, default=True) timeout = db.Column(Integer, default=15) username = db.Column(db.SmallString) password = db.Column(db.SmallString) __mapper_args__ = {"polymorphic_identity": "rest_call_service"} def job(self, run, payload, device=None): local_variables = locals() rest_url = run.sub(run.rest_url, local_variables) run.log("info", f"Sending REST Call to {rest_url}", device, logger="security") kwargs = { p: run.sub(getattr(self, p), local_variables) for p in ("headers", "params", "timeout") } kwargs["verify"] = run.verify_ssl_certificate if self.username: kwargs["auth"] = HTTPBasicAuth( self.username, app.get_password(self.password) ) if run.call_type in ("POST", "PUT", "PATCH"): kwargs["json"] = run.sub(run.payload, local_variables) call = getattr(app.request_session, run.call_type.lower()) response = call(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["result"] = "Wrong credentials supplied." return result return { "url": rest_url, "status_code": response.status_code, "headers": dict(response.headers), "result": response.text, }
class Node(ViewObject): __tablename__ = class_type = "node" __mapper_args__ = {"polymorphic_identity": "node"} parent_type = "view_object" id = db.Column(Integer, ForeignKey(ViewObject.id), primary_key=True) model = db.Column(db.SmallString) device_id = db.Column(Integer, ForeignKey("device.id")) device = relationship("Device", foreign_keys="Node.device_id") device_name = association_proxy("device", "name") def __init__(self, **kwargs): super().__init__(**kwargs) if not self.name: self.name = vs.get_time()
class Object(AbstractBase): __tablename__ = "object" type = db.Column(db.SmallString) __mapper_args__ = { "polymorphic_identity": "object", "polymorphic_on": type } id = db.Column(Integer, primary_key=True) public = db.Column(Boolean) last_modified = db.Column(db.TinyString, info={"log_change": False}) subtype = db.Column(db.SmallString) description = db.Column(db.SmallString) model = db.Column(db.SmallString) location = db.Column(db.SmallString) vendor = db.Column(db.SmallString) def update(self, **kwargs): super().update(**kwargs) if kwargs.get("dont_update_pools", False): return for pool in db.fetch_all("pool"): if pool.manually_defined or not pool.compute(self.class_type): continue match = pool.object_match(self) relation, number = f"{self.class_type}s", f"{self.class_type}_number" if match and self not in getattr(pool, relation): getattr(pool, relation).append(self) setattr(pool, number, getattr(pool, number) + 1) if self in getattr(pool, relation) and not match: getattr(pool, relation).remove(self) setattr(pool, number, getattr(pool, number) - 1) def delete(self): number = f"{self.class_type}_number" for pool in self.pools: setattr(pool, number, getattr(pool, number) - 1) @classmethod def rbac_filter(cls, query, mode, user): public_objects = query.filter(cls.public == true()) user_objects = (query.join(cls.pools).join( models["access"], models["pool"].access).join( models["user"], models["access"].users).filter( models["access"].pools_access.contains(mode)).filter( models["user"].name == user.name)) user_group_objects = (query.join(cls.pools).join( models["access"], models["pool"].access).join( models["group"], models["access"].groups).join( models["user"], models["group"].users).filter( models["access"].pools_access.contains(mode)).filter( models["user"].name == user.name)) return public_objects.union(user_objects, user_group_objects)
def set_pool_properties(Pool): for property in properties["filtering"]["device"]: setattr(Pool, f"device_{property}", db.Column(db.LargeString)) setattr( Pool, f"device_{property}_match", db.Column(db.TinyString, default="inclusion"), ) for property in properties["filtering"]["link"]: setattr(Pool, f"link_{property}", db.Column(db.LargeString)) setattr( Pool, f"link_{property}_match", db.Column(db.TinyString, default="inclusion"), ) return Pool
class NapalmBackupService(ConnectionService): __tablename__ = "napalm_backup_service" pretty_name = "NAPALM Data Backup" parent_type = "connection_service" id = db.Column(Integer, ForeignKey("connection_service.id"), primary_key=True) driver = db.Column(db.SmallString) use_device_driver = db.Column(Boolean, default=True) timeout = db.Column(Integer, default=60) optional_args = db.Column(db.Dict) property = db.Column(db.SmallString) getters = db.Column(db.List) replacements = db.Column(db.List) __mapper_args__ = {"polymorphic_identity": "napalm_backup_service"} def job(self, run, device): path = Path.cwd() / "network_data" / device.name path.mkdir(parents=True, exist_ok=True) try: runtime = datetime.now() setattr(device, f"last_{self.property}_runtime", str(runtime)) napalm_connection = run.napalm_connection(device) run.log("info", f"Fetching getters: {', '.join(run.getters)}", device) result = {} for getter in run.getters: try: output = vs.dict_to_string( getattr(napalm_connection, getter)()) for replacement in self.replacements: output = sub( replacement["pattern"], replacement["replace_with"], output, flags=M, ) result[getter] = output except Exception as exc: result[getter] = f"{getter} failed because of {exc}" result = vs.dict_to_string(result) setattr(device, self.property, result) with open(path / self.property, "w") as file: file.write(result) setattr(device, f"last_{self.property}_status", "Success") duration = f"{(datetime.now() - runtime).total_seconds()}s" setattr(device, f"last_{self.property}_duration", duration) setattr(device, f"last_{self.property}_update", str(runtime)) run.update_configuration_properties(path, self.property, device) except Exception as exc: setattr(device, f"last_{self.property}_status", "Failure") setattr(device, f"last_{self.property}_failure", str(runtime)) run.update_configuration_properties(path, self.property, device) return {"success": False, "result": str(exc)} return {"success": True}
class User(AbstractBase, UserMixin): __tablename__ = type = "user" id = db.Column(Integer, primary_key=True) name = db.Column(db.SmallString, unique=True) email = db.Column(db.SmallString) permissions = db.Column(db.List) password = db.Column(db.SmallString) group = db.Column(db.SmallString) small_menu = db.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 GitService(Service): __tablename__ = "git_service" pretty_name = "Git Action" id = db.Column(Integer, ForeignKey("service.id"), primary_key=True) git_repository = db.Column(db.SmallString) relative_path = db.Column(Boolean, default=False) pull = db.Column(Boolean, default=False) add_commit = db.Column(Boolean, default=False) commit_message = db.Column(db.LargeString) push = db.Column(Boolean, default=False) __mapper_args__ = {"polymorphic_identity": "git_service"} def job(self, run, payload, device=None): repo = Repo( Path.cwd() / self.git_repository if self.relative_path else self.git_repository ) if self.add_commit: repo.git.add(A=True) repo.git.commit(m=self.commit_message) if self.pull: repo.remotes.origin.pull() if self.push: repo.remotes.origin.push() return {"success": True}
class Event(AbstractBase): __tablename__ = type = "event" id = db.Column(Integer, primary_key=True) name = db.Column(db.SmallString, unique=True) log_source = db.Column(db.SmallString) log_source_regex = db.Column(Boolean, default=False) log_content = db.Column(db.SmallString) log_content_regex = db.Column(Boolean, default=False) service_id = db.Column(Integer, ForeignKey("service.id")) service = relationship("Service", back_populates="events") service_name = association_proxy("service", "name") def match_log(self, source, content): source_match = ( search(self.log_source, source) if self.log_source_regex else self.log_source in source ) content_match = ( search(self.log_content, content) if self.log_content_regex else self.log_content in content ) if source_match and content_match: self.service.run()
class NapalmConfigurationService(ConnectionService): __tablename__ = "napalm_configuration_service" pretty_name = "NAPALM Configuration" parent_type = "connection_service" id = db.Column(Integer, ForeignKey("connection_service.id"), primary_key=True) action = db.Column(db.SmallString) content = db.Column(db.LargeString) driver = db.Column(db.SmallString) use_device_driver = db.Column(Boolean, default=True) timeout = db.Column(Integer, default=60) optional_args = db.Column(db.Dict) __mapper_args__ = {"polymorphic_identity": "napalm_configuration_service"} def job(self, run, payload, device): napalm_connection = run.napalm_connection(device) run.log( "info", "Pushing Configuration with NAPALM", device, logger="security", ) config = "\n".join(run.sub(run.content, locals()).splitlines()) getattr(napalm_connection, run.action)(config=config) napalm_connection.commit_config() return {"success": True, "result": f"Config push ({config})"}
class Group(AbstractBase): __tablename__ = type = "group" id = db.Column(Integer, primary_key=True) name = db.Column(db.SmallString, unique=True) email = db.Column(db.SmallString) users = relationship("User", secondary=db.user_group_table, back_populates="groups") access = relationship("Access", secondary=db.access_group_table, back_populates="groups") def update(self, **kwargs): old_users = set(self.users) super().update(**kwargs) for user in old_users | set(self.users): user.update_rbac()
def database_init(cls): for property in app.configuration_properties: for timestamp in app.configuration_timestamps: setattr( cls, f"last_{property}_{timestamp}", db.Column(db.SmallString, default="Never"), ) return cls
class SwissArmyKnifeService(Service): __tablename__ = "swiss_army_knife_service" pretty_name = "Swiss Army Knife" id = db.Column(Integer, ForeignKey("service.id"), primary_key=True) __mapper_args__ = {"polymorphic_identity": "swiss_army_knife_service"} def job(self, *args, **kwargs): return getattr(self, self.scoped_name)(*args, **kwargs) def Start(self, *args, **kwargs): # noqa: N802 return {"success": True} def End(self, *args, **kwargs): # noqa: N802 return {"success": True} def Placeholder(self, *args, **kwargs): # noqa: N802 return {"success": True} def cluster_monitoring(self, run, payload): protocol = app.settings["cluster"]["scan_protocol"] for instance in db.fetch_all("instance"): db.factory( "instance", **get( f"{protocol}://{instance.ip_address}/rest/is_alive", timeout=app.settings["cluster"]["scan_timeout"], ).json(), ) return {"success": True} def git_push_configurations(self, run, payload, device=None): if not app.settings["app"]["git_repository"]: return repo = Repo(Path.cwd() / "network_data") try: repo.remotes.origin.pull() repo.git.add(A=True) repo.git.commit(m="Automatic commit (configurations)") except GitCommandError as exc: info(f"Git commit failed ({str(exc)}") repo.remotes.origin.push() return {"success": True} def process_payload1(self, run, payload, device): get_facts = run.get_result("NAPALM: Get Facts", device.name) get_interfaces = run.get_result("NAPALM: Get interfaces", device.name) uptime_less_than_50000 = get_facts["result"]["get_facts"][ "uptime"] < 50000 mgmg1_is_up = get_interfaces["result"]["get_interfaces"][ "Management1"]["is_up"] return { "success": True, "uptime_less_5000": uptime_less_than_50000, "Management1 is UP": mgmg1_is_up, }
class PingService(Service): __tablename__ = "ping_service" pretty_name = "ICMP / TCP Ping" id = db.Column(Integer, ForeignKey("service.id"), primary_key=True) protocol = db.Column(db.SmallString) ports = db.Column(db.SmallString) count = db.Column(Integer, default=5) timeout = db.Column(Integer, default=2) ttl = db.Column(Integer, default=60) packet_size = db.Column(Integer, default=56) __mapper_args__ = {"polymorphic_identity": "ping_service"} def job(self, run, device): if run.protocol == "ICMP": command = ["ping"] for variable, property in ( ("c", "count"), ("W", "timeout"), ("t", "ttl"), ("s", "packet_size"), ): value = getattr(self, property) if value: command.extend(f"-{variable} {value}".split()) command.append(device.ip_address) run.log("info", f"Running PING ({command})", device) output = check_output(command).decode().strip().splitlines() 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}