def change_object(self, object_id: str, v: Dict[str, Any]): self.logger.debug("Changed object: %s", v) # See: https://code.getnoc.com/noc/noc/merge_requests/49 try: o: ObjectM = self.model.objects.get(pk=object_id) except self.model.DoesNotExist: self.logger.error("Cannot change %s:%s: Does not exists", self.name, object_id) return None if "name" in v and v["name"] != o.name: o.name = v["name"] if "model" in v and isinstance(v["model"], str): # Fix if model is name? v["model"] = ObjectModel.get_by_name(v["model"]) if "model" in v and v["model"].name != o.model.name: o.model = v["model"] if (not o.container and v.get("container")) or ( v.get("container") and v["container"] != str(o.container.id) ): o.container = v["container"] if "data" not in v or not v["data"]: # reset only RemoteSystem Scope # o.data = [] for item in o.data: if item.scope == self.system.name: o.reset_data(interface=item.interface, key=item.attr, scope=self.system.name) elif v["data"]: self.merge_data(o, v["data"]) o.save() return o
def api_add_group(self, request, type, name, container=None, serial=None): if is_objectid(container): c = Object.get_by_id(container) if not c: return self.response_not_found() c = c.id elif container: return self.response_bad_request() m = ObjectModel.get_by_id(type) if not m: return self.response_not_found() o = Object(name=name, model=m, container=c) if serial and m.get_data("asset", "part_no0"): o.set_data("asset", "serial", serial) o.save() o.log("Created", user=request.user.username, system="WEB", op="CREATE") return str(o.id)
def get_data(self, **kwargs): self.model_name = {} # oid -> name data = list(Object._get_collection().aggregate([{ "$group": { "_id": "$model", "total": { "$sum": 1 } } }])) oms = [x["_id"] for x in data if x["_id"]] c = ObjectModel._get_collection() om_names = {} while oms: chunk, oms = oms[:500], oms[500:] om_names.update({ x["_id"]: x["name"] for x in c.find({"_id": { "$in": chunk }}, { "_id": 1, "name": 1 }) }) data = sorted( ([om_names[x["_id"]], x["total"]] for x in data if x["_id"] in om_names), key=lambda x: -x[1], ) return self.from_dataset( title=self.title, columns=[ "Model", TableColumn("Count", format="numeric", align="right", total="sum") ], data=data, enumerate=True, )
def api_connect( self, request, object, name, remote_object, remote_name, cable: Optional[str] = None, reconnect=False, ): lo: Object = self.get_object_or_404(Object, id=object) ro: Object = self.get_object_or_404(Object, id=remote_object) cable_o: Optional[Object] = None if cable: cable = ObjectModel.get_by_name(cable) cable_o = Object( name="Wire %s:%s <-> %s:%s" % (lo.name, name, ro.name, remote_name), model=cable, container=lo.container.id, ) cable_o.save() print(lo, ro, cable_o) try: if cable_o: c1, c2 = cable_o.model.connections[:2] self.logger.debug("Wired connect c1:c2", c1, c2) lo.connect_p2p(name, cable_o, c1.name, {}, reconnect=reconnect) ro.connect_p2p(remote_name, cable_o, c2.name, {}, reconnect=reconnect) lo.save() ro.save() else: lo.connect_p2p(name, ro, remote_name, {}, reconnect=reconnect) except ConnectionError as e: self.logger.warning("Connection Error: %s", str(e)) return self.render_json({"status": False, "text": str(e)}) return True
def submit( self, type: str, part_no: List[str], number: Optional[str] = None, builtin: bool = False, vendor: Optional[str] = None, revision: Optional[str] = None, serial: Optional[str] = None, mfg_date: Optional[str] = None, description: Optional[str] = None, ): # Check the vendor and the serial are sane # OEM transceivers return binary trash often if vendor: # Possible dead code try: vendor.encode("utf-8") except UnicodeDecodeError: self.logger.info("Trash submited as vendor id: %s", vendor.encode("hex")) return if serial: # Possible dead code try: serial.encode("utf-8") except UnicodeDecodeError: self.logger.info("Trash submited as serial: %s", serial.encode("hex")) return # is_unknown_xcvr = not builtin and part_no[0].startswith( "Unknown | Transceiver | ") if not type and is_unknown_xcvr: type = "XCVR" # Skip builtin modules if builtin: # Adjust context anyway self.prepare_context(type, number) return # Builtin must aways have type set # if is_unknown_xcvr: self.logger.info("%s S/N %s should be resolved later", part_no[0], serial) self.prepare_context(type, number) self.objects += [("XCVR", part_no[0], self.ctx.copy(), serial)] return # Cache description if description: for p in part_no: if p not in self.pn_description: self.pn_description[p] = description # Find vendor vnd = self.get_vendor(vendor) if not vnd: # Try to resolve via model map m = self.get_model_map(vendor, part_no, serial) if not m: self.logger.error("Unknown vendor '%s' for S/N %s (%s)", vendor, serial, description) return else: # Find model m = ObjectModel.get_model(vnd, part_no) if not m: # Try to resolve via model map m = self.get_model_map(vendor, part_no, serial) if not m: self.logger.info( "Unknown model: vendor=%s, part_no=%s (%s). " "Skipping", vnd.name, part_no, description, ) self.register_unknown_part_no(vnd, part_no, description) return # Sanitize serial number against the model serial = self.clean_serial(m, number, serial) # if m.cr_context and type != m.cr_context: # Override type with object mode's one self.logger.info("Model changes type to '%s'", m.cr_context) type = m.cr_context if not type: self.logger.info( "Cannot resolve type for: vendor=%s, part_no=%s (%s). " "Skipping", vnd.name, description, part_no, ) return self.prepare_context(type, number) # Get connection rule if not self.rule and m.connection_rule: self.set_rule(m.connection_rule) # Set initial context if type in self.rule_context: scope = self.rule_context[type][0] if scope: self.set_context(scope, number) # Find existing object or create new o: Optional["Object"] = Object.objects.filter(model=m.id, data__match={ "interface": "asset", "attr": "serial", "value": serial }).first() if not o: # Create new object self.logger.info("Creating new object. model='%s', serial='%s'", m.name, serial) data = [ ObjectAttr(scope="", interface="asset", attr="serial", value=serial) ] if revision: data += [ ObjectAttr(scope="", interface="asset", attr="revision", value=revision) ] if mfg_date: data += [ ObjectAttr(scope="", interface="asset", attr="mfg_date", value=mfg_date) ] if self.object.container: container = self.object.container.id else: container = self.lost_and_found o = Object(model=m, data=data, container=container) o.save() o.log( "Created by asset_discovery", system="DISCOVERY", managed_object=self.object, op="CREATE", ) else: # Add all connection to disconnect list self.to_disconnect.update( set((o, c[0], c[1], c[2]) for c in o.iter_inner_connections())) # Check revision if o.get_data("asset", "revision") != revision: # Update revision self.logger.info( "Object revision changed [%s %s] %s -> %s", m.name, o.id, o.get_data("asset", "revision"), revision, ) o.set_data("asset", "revision", revision) o.save() o.log( "Object revision changed: %s -> %s" % (o.get_data("asset", "revision"), revision), system="DISCOVERY", managed_object=self.object, op="CHANGE", ) # Check manufacturing date if mfg_date and o.get_data("asset", "revision") != revision: # Update revision self.logger.info( "Object manufacturing date changed [%s %s] %s -> %s", m.name, o.id, o.get_data("asset", "mfg_date"), mfg_date, ) o.set_data("asset", "mfg_date", mfg_date) o.save() o.log( "Object manufacturing date: %s -> %s" % (o.get_data("asset", "mfg_date"), mfg_date), system="DISCOVERY", managed_object=self.object, op="CHANGE", ) # Check management if o.get_data("management", "managed"): if o.get_data("management", "managed_object") != self.object.id: self.logger.info("Changing object management to '%s'", self.object.name) o.set_data("management", "managed_object", self.object.id) o.save() o.log( "Management granted", system="DISCOVERY", managed_object=self.object, op="CHANGE", ) self.update_name(o) if o.id in self.managed: self.managed.remove(o.id) self.objects += [(type, o, self.ctx.copy(), serial)] # Collect stack members if number and o.get_data("stack", "stackable"): self.stack_member[o] = number
def api_node(self, request): children = [] if request.GET and "node" in request.GET: container = request.GET["node"] if is_objectid(container): container = Object.get_by_id(container) if not container: return self.response_not_found() children = [ (o.name, o) for o in Object.objects.filter(container=container.id) ] # Collect inner connections children += [ (name, o) for name, o, _ in container.get_inner_connections() ] elif container == "root": cmodels = [ d["_id"] for d in ObjectModel._get_collection().find( {"data.container.container": True}, {"_id": 1}) ] children = [(o.name, o) for o in Object.objects.filter(__raw__={ "container": None, "model": { "$in": cmodels } })] else: return self.response_bad_request() r = [] # Build node interface for name, o in children: m_plugins = o.model.plugins or [] disabled_plugins = set(p[1:] for p in m_plugins if p.startswith("-")) n = { "id": str(o.id), "name": name, "plugins": [], "can_add": bool(o.get_data("container", "container")), "can_delete": str(o.model.uuid) not in self.UNDELETABLE } if (o.get_data("container", "container") or o.has_inner_connections()): # n["expanded"] = Object.objects.filter(container=o.id).count() == 1 n["expanded"] = False else: n["leaf"] = True if o.get_data("rack", "units"): n["plugins"] += [self.get_plugin_data("rack")] if o.model.connections: n["plugins"] += [self.get_plugin_data("inventory")] if o.get_data("geopoint", "layer"): n["plugins"] += [self.get_plugin_data("map")] if o.get_data("management", "managed_object"): n["plugins"] += [self.get_plugin_data("managedobject")] if o.get_data("contacts", "has_contacts"): n["plugins"] += [self.get_plugin_data("contacts")] # Append model's plugins for p in m_plugins: if not p.startswith("-"): n["plugins"] += [self.get_plugin_data(p)] n["plugins"] += [ self.get_plugin_data("data"), self.get_plugin_data("comment"), self.get_plugin_data("file"), self.get_plugin_data("log") ] # Process disabled plugins n["plugins"] = [ p for p in n["plugins"] if p["name"] not in disabled_plugins ] r += [n] return r
def submit(self, type, part_no, number=None, builtin=False, vendor=None, revision=None, serial=None, description=None): # Check the vendor and the serial are sane # OEM transceivers return binary trash often if vendor: try: vendor.encode("utf-8") except UnicodeDecodeError: self.info("Trash submited as vendor id: %s" % vendor.encode("hex")) return if serial: try: serial.encode("utf-8") except UnicodeDecodeError: self.info("Trash submited as serial: %s" % serial.encode("hex")) return # is_unknown_xcvr = (not builtin and part_no[0].startswith("Unknown | Transceiver | ")) if not type and is_unknown_xcvr: type = "XCVR" # Skip builtin modules if builtin: # Adjust context anyway self.prepare_context(type, number) return # Builtin must aways have type set # if is_unknown_xcvr: self.debug("%s S/N %s should be resolved later" % (part_no[0], serial)) self.prepare_context(type, number) self.objects += [("XCVR", part_no[0], self.ctx.copy(), serial)] return # Cache description if description: for p in part_no: if p not in self.pn_description: self.pn_description[p] = description # Find vendor vnd = self.get_vendor(vendor) if not vnd: # Try to resolve via model map m = self.get_model_map(vendor, part_no, serial) if not m: self.error("Unknown vendor '%s' for S/N %s (%s)" % (vendor, serial, description)) return else: # Find model m = ObjectModel.get_model(vnd, part_no) if not m: # Try to resolve via model map m = self.get_model_map(vendor, part_no, serial) if not m: self.debug( "Unknown model: vendor=%s, part_no=%s (%s). Skipping" % (vnd.name, description, part_no)) self.register_unknown_part_no(vnd, part_no, description) return if m.cr_context and type != m.cr_context: # Override type with object mode's one self.debug("Model changes type to '%s'" % m.cr_context) type = m.cr_context if not type: self.debug( "Cannot resolve type for: vendor=%s, part_no=%s (%s). Skipping" % (vnd.name, description, part_no)) return self.prepare_context(type, number) # Get connection rule if not self.rule and m.connection_rule: self.set_rule(m.connection_rule) # Set initial context if type in self.rule_context: scope = self.rule_context[type][0] if scope: self.set_context(scope, number) # if not serial or serial == "None": serial = self.generate_serial(m, number) self.info("Generating virtual serial: %s" % serial) # Find existing object or create new o = Object.objects.filter(model=m.id, data__asset__serial=serial).first() if not o: # Create new object self.info("Creating new object. model='%s', serial='%s'" % (m.name, serial)) data = {"asset": {"serial": serial}} if revision: data["asset"]["revision"] = revision o = Object(model=m, data=data, container=self.lost_and_found) o.save() o.log("Created by asset_discovery", system="DISCOVERY", managed_object=self.object, op="CREATE") # Check revision if o.get_data("asset", "revision") != revision: # Update revision self.info( "Object revision changed [%s %s] %s -> %s" % (m.name, o.id, o.get_data("asset", "revision"), revision)) o.set_data("asset", "revision", revision) o.save() o.log("Object revision changed: %s -> %s" % (o.get_data("asset", "revision"), revision), system="DISCOVERY", managed_object=self.object, op="CHANGE") # Check management if o.get_data("management", "managed"): if o.get_data("management", "managed_object") != self.object.id: self.info("Changing object management to '%s'" % self.object.name) o.set_data("management", "managed_object", self.object.id) o.save() o.log("Management granted", system="DISCOVERY", managed_object=self.object, op="CHANGE") self.update_name(o) if o.id in self.managed: self.managed.remove(o.id) self.objects += [(type, o, self.ctx.copy(), serial)] # Collect stack members if number and o.get_data("stack", "stackable"): self.stack_member[o] = number
def api_get_crossing_proposals( self, request, o1, o2=None, left_filter: Optional[str] = None, right_filter: Optional[str] = None, cable_filter: Optional[str] = None, ): """ API for connnection form. 1) If cable_filter set, checked connection capable with cable. 2) If left_filter set, check renmote object :param request: :param o1: :param o2: :param left_filter: :param right_filter: :param cable_filter: :return: """ self.logger.info( "Crossing proposals: %s:%s, %s:%s. Cable: %s", o1, left_filter, o2, right_filter, cable_filter, ) lo: Object = self.get_object_or_404(Object, id=o1) ro: Optional[Object] = None if o2: ro = self.get_object_or_404(Object, id=o2) lcs: List[Dict[str, Any]] = [] cable: Optional[ObjectModel] = None # Getting cable cables = ObjectModel.objects.filter(data__length__length__gte=0) if cable_filter: cable = ObjectModel.get_by_name(cable_filter) for c in lo.model.connections: valid, disable_reason = True, "" if cable_filter: # If select cable_filter - check every connection to cable cable_connections = [ c for c in lo.model.get_connection_proposals(c.name) if c[0] == cable.id ] valid = bool(cable_connections) elif ro and right_filter: rc = ro.model.get_model_connection(right_filter) if not rc: raise valid, disable_reason = lo.model.check_connection(c, rc) elif ro: valid = bool([ c for c in lo.model.get_connection_proposals(c.name) if c[0] == ro.model.id ]) oc, oo, _ = lo.get_p2p_connection(c.name) lcs += [{ "name": c.name, "type": str(c.type.id), "type__label": c.type.name, "gender": c.gender, "direction": c.direction, "protocols": c.protocols, "free": not bool(oc), "valid": valid, "disable_reason": disable_reason, }] rcs: List[Dict[str, Any]] = [] if ro: for c in ro.model.connections: valid, disable_reason = True, "" if cable_filter: cable_connections = [ c for c in ro.model.get_connection_proposals(c.name) if c[0] == cable.id ] valid = bool(cable_connections) elif left_filter: lc = lo.model.get_model_connection(left_filter) if not lc: raise valid, disable_reason = lo.model.check_connection(c, lc) else: valid = bool([ c for c in ro.model.get_connection_proposals(c.name) if c[0] == lo.model.id ]) oc, oo, _ = ro.get_p2p_connection(c.name) rcs += [{ "name": c.name, "type": str(c.type.id), "type__label": c.type.name, "gender": c.gender, "direction": c.direction, "protocols": c.protocols, "free": not bool(oc), "valid": valid, "disable_reason": disable_reason, }] # Forming cable return { "left": { "connections": lcs }, "right": { "connections": rcs }, "cable": [{ "name": c.name, "available": True } for c in cables], "valid": lcs and rcs and left_filter and right_filter, }
def clean_model(name): r = ObjectModel.get_by_name(name) if not r: raise ValueError(f"Model {name} not found") return r