class Reboot(Document): meta = { "collection": "noc.fm.reboots", "indexes": ["object", ("object", "ts")] } object = IntField() ts = DateTimeField() # Recovered time last = DateTimeField() # Last up timestamp def __unicode__(self): return u"%d" % self.object @classmethod def register(cls, managed_object, ts=None, last=None): """ Register reboot. Populated via Uptime.register(...) :param managed_object: Managed object reference :param ts: Recover time :params last: Last seen time """ oid = managed_object.id if not ts: ts = datetime.datetime.now() if not last: last = ts logger.debug("[%s] Register reboot at %s", managed_object.name, ts) cls._get_collection().insert({ "object": oid, "ts": ts, "last": last })
class L3Link(Document): """ Network L3 links. Always contains a list of subinterface references """ meta = { "collection": "noc.links", "strict": False, "auto_create_index": False, "indexes": ["subinterfaces", "linked_objects"] } subinterfaces = PlainReferenceListField("inv.SubInterface") # List of linked objects linked_objects = ListField(IntField()) # Name of discovery method or "manual" discovery_method = StringField() # Timestamp of first discovery first_discovered = DateTimeField(default=datetime.datetime.now) # Timestamp of last confirmation last_seen = DateTimeField() # L3 path cost l3_cost = IntField(default=1) def __unicode__(self): return u"(%s)" % ", ".join([unicode(i) for i in self.subinterfaces]) def clean(self): self.linked_objects = sorted( set(i.managed_object.id for i in self.subinterfaces)) super(L3Link, self).clean()
class FailedScriptLog(Document): meta = { "collection": "noc.log.sa.failed_scripts", "allow_inheritance": False, "indexes": ["-timestamp", { "fields": ["expires"], "expireAfterSeconds": 0 }] } timestamp = DateTimeField() managed_object = StringField() address = StringField() script = StringField() error_code = IntField() error_text = StringField() expires = DateTimeField() def __unicode__(self): return str(self.id)
class Outage(Document): meta = { "collection": "noc.fm.outages", "allow_inheritance": False, "indexes": ["object", "start"] } object = IntField() start = DateTimeField() stop = DateTimeField() # None for active outages def __unicode__(self): return u"%d" % self.object @property def is_active(self): return self.stop is None @classmethod def register_outage(cls, object, status): """ Change current outage status :param cls: :param object: Managed Object :param status: True - if object is down, False - otherwise :return: """ ts = datetime.datetime.now() o = cls.objects.filter( object=object.id, start__lte=datetime.datetime.now()).order_by("-start").first() if o and o.is_active and not status: # Close active outage o.stop = ts o.save() elif status and ((o and not o.is_active) or not o): # Create new outage Outage(object=object.id, start=ts, stop=None).save()
class Discovery(Document): meta = { "collection": "noc.schedules.inv.discovery", "strict": False, "auto_create_index": False } job_class = StringField(db_field='jcls') schedule = DictField() ts = DateTimeField(db_field='ts') last = DateTimeField() last_success = DateTimeField(db_field='st') last_duration = FloatField(db_field='ldur') last_status = StringField(db_field='ls') status = StringField(db_field='s') managed_object = ForeignKeyField(ManagedObject, db_field='key') data = DictField() traceback = DictField() runs = IntField() faults = IntField(db_field='f') log = ListField() def __unicode__(self): return "%s: %s" % (self.managed_object, self.job_class)
class MACLog(Document): """ Customer MAC address changes """ meta = { "collection": "noc.mac_log", "allow_inheritance": False, "indexes": ["mac", "-timestamp"] } # Todo: Add Validation timestamp = DateTimeField() mac = StringField() vc_domain_name = StringField() vlan = IntField() managed_object_name = StringField() interface_name = StringField()
class Cache(Document): meta = { "collection": "noc.cache", "allow_inheritance": False, "indexes": [ { "fields": ["expires"], "expireAfterSeconds": 0 } ] } key = StringField(db_field="_id", primary_key=True) value = BinaryField(db_field="v") pickled_value = BinaryField(db_field="p") expires = DateTimeField(db_field="e") def __unicode__(self): return unicode(self.key)
class PendingLinkCheck(Document): """ Customer MAC address changes """ meta = { "collection": "noc.inv.pending_link_check", "allow_inheritance": False, "indexes": [("method", "local_object")] } method = StringField() local_object = ForeignKeyField(ManagedObject) local_interface = StringField() # optional remote_object = ForeignKeyField(ManagedObject) remote_interface = StringField() expire = DateTimeField() def __unicode__(self): return u"%s:%s:%s:%s:%s" % ( self.method, self.local_object.name, self.local_interface, self.remote_object.name, self.remote_interface) @classmethod def submit(cls, method, local_object, local_interface, remote_object, remote_interface): expire = datetime.datetime.now() + datetime.timedelta(days=2) plc = PendingLinkCheck.objects.filter( method=method, local_object=local_object.id, local_interface=local_interface, remote_object=remote_object.id, remote_interface=remote_interface).first() if plc: plc.expire = expire else: plc = cls(method=method, local_object=local_object.id, local_interface=local_interface, remote_object=remote_object.id, remote_interface=remote_interface, expire=expire) plc.save()
class Link(Document): """ Network links. Always contains a list of 2*N references. 2 - for fully resolved links 2*N for unresolved N-link portchannel N, N > 2 - broadcast media """ meta = { "collection": "noc.links", "strict": False, "auto_create_index": False, "indexes": ["interfaces", "linked_objects", "linked_segments"] } # Optional link name name = StringField() # Optional description description = StringField() # Optional shape shape = StringField() # List of interfaces interfaces = PlainReferenceListField("inv.Interface") # Link type, detected automatically type = StringField( choices=[ # 2 managed objects, 2 linked interfaces ("p", "Point-to-Point"), # 2 managed objects, even number of linked interfaces (>2) ("a", "Point-to-Point Aggregated"), # >2 managed objects, one uplink ("m", "Point-to-Multipoint"), # >2 managed objects, no dedicated uplink ("M", "Multipoint-to-Multipoint"), # Unknown ("u", "Unknown") ], default="u") # List of linked objects linked_objects = ListField(IntField()) # List of linked segments linked_segments = ListField(ObjectIdField()) # Name of discovery method or "manual" discovery_method = StringField() # Timestamp of first discovery first_discovered = DateTimeField(default=datetime.datetime.now) # Timestamp of last confirmation last_seen = DateTimeField() # L2 path cost l2_cost = IntField(default=1) # L3 path cost l3_cost = IntField(default=1) def __unicode__(self): if self.interfaces: return u"(%s)" % ", ".join(unicode(i) for i in self.interfaces) else: return u"Stale link (%s)" % self.id def iter_changed_datastream(self): if config.datastream.enable_managedobject: for mo_id in self.linked_objects: yield "managedobject", mo_id def clean(self): self.linked_objects = sorted( set(i.managed_object.id for i in self.interfaces)) self.linked_segments = sorted( set(i.managed_object.segment.id for i in self.interfaces)) self.type = self.get_type() def contains(self, iface): """ Check link contains interface :return: boolean """ return iface in self.interfaces @property def is_ptp(self): """ Check link is point-to-point link :return: """ return self.type == "p" or self.type == "a" @property def is_lag(self): """ Check link is unresolved LAG :return: """ return self.type == "p" or self.type == "a" @property def is_broadcast(self): """ Check link is broadcast media :return: """ return not self.is_ptp and not self.is_lag @property def is_loop(self): """ Check link is looping to same object :return: """ return len(self.linked_objects) == 1 @property def interface_ids(self): """ Returns list of interface ids, avoiding dereference :return: """ def q(i): if hasattr(i, "id"): return i.id return i return [q(iface) for iface in self._data.get("interfaces", [])] def other(self, interface): """ Return other interfaces of the link :param interface: :return: """ return [i for i in self.interfaces if i.id != interface.id] def other_ptp(self, interface): """ Return other interface of ptp link :param interface: :return: """ return self.other(interface)[0] def touch(self, method=None): """ Touch last_seen """ now = datetime.datetime.now() op = {"last_seen": now} self.last_seen = now if method: self.discovery_method = method op["discovery_method"] = method # Do not save to prevent rebuilding topology self._get_collection().update({"_id": self.id}, {"$set": op}) # self.save() @classmethod def object_links(cls, object): return Link.objects.filter(linked_objects=object.id) @classmethod def object_links_count(cls, object): return Link.objects.filter(linked_objects=object.id).count() def on_save(self): if not hasattr( self, "_changed_fields") or "interfaces" in self._changed_fields: self.update_topology() def on_delete(self): self.update_topology() @property def managed_objects(self): """ List of connected managed objects """ from noc.sa.models.managedobject import ManagedObject return list(ManagedObject.objects.filter(id__in=self.linked_objects)) @property def segments(self): """ List of segments connected by link :return: """ from noc.inv.models.networksegment import NetworkSegment return list(NetworkSegment.objects.filter(id__in=self.linked_segments)) def update_topology(self): for mo in self.managed_objects: mo.update_topology() def get_type(self): """ Detect link type :return: Link type as value for .type """ n_objects = len(self.linked_objects) n_interfaces = len(self.interfaces) if n_objects == 2 and n_interfaces == 2: return "p" # Point-to-point if n_objects == 2 and n_interfaces > 2 and n_interfaces % 2 == 0: d = defaultdict(int) # object -> count for i in self.interfaces: d[i.managed_object.id] += 1 k = d.keys() if d[k[0]] == d[k[1]]: return "a" # Point-to-Point aggregated if n_objects > 2: if self.type == "m": return "m" else: return "M" return "u"
class Uptime(Document): meta = { "collection": "noc.fm.uptimes", "indexes": ["object", ("object", "stop")] } object = IntField() start = DateTimeField() stop = DateTimeField() # None for active uptime last = DateTimeField() # Last update last_value = FloatField() # Last registred value SEC = datetime.timedelta(seconds=1) FWRAP = float((1 << 32) - 1) / 100.0 WRAP = datetime.timedelta(seconds=FWRAP) WPREC = 0.1 # Wrap precision def __unicode__(self): return u"%d" % self.object @classmethod def is_reboot(cls, old_uptime, new_uptime): """ Returns true if reboot detected :param old_uptime: :param new_uptime: :return: """ if old_uptime > new_uptime: # Check for counter wrap return True return False @classmethod def register(cls, managed_object, uptime): """ Register uptime :param managed_object: Managed object reference :param uptime: Registered uptime in seconds """ if not uptime: return oid = managed_object.id now = datetime.datetime.now() delta = datetime.timedelta(seconds=uptime) logger.debug("[%s] Register uptime %s", managed_object.name, delta) # Update data c = cls._get_collection() d = c.find_one({"object": oid, "stop": None}) if d: # Check for reboot is_rebooted = False if d["last_value"] > uptime: # Check for counter wrapping # Get wrapped delta dl = cls.FWRAP - d["last_value"] + uptime # Get timestamp delta tsd = total_seconds(now - d["last"]) if abs(dl - tsd) > tsd * cls.WPREC: is_rebooted = True else: logger.debug("Counter wrap detected") if is_rebooted: # Reboot registered # Closing existing uptime ts = now - delta logger.debug("[%s] Closing uptime (%s - %s, delta %s)", managed_object.name, d["start"], ts - cls.SEC, delta) c.update({"_id": d["_id"]}, {"$set": {"stop": ts - cls.SEC}}) # Start new uptime logger.debug("[%s] Starting new uptime from %s", managed_object.name, ts) c.insert({ "object": oid, "start": ts, "stop": None, "last": now, "last_value": uptime }) # Reboot.register(managed_object, ts, d["last"]) else: logger.debug("[%s] Refreshing existing uptime (%s - %s)", managed_object.name, d["start"], now) c.update({"_id": d["_id"]}, {"$set": { "last": now, "last_value": uptime }}) else: # First uptime logger.debug("[%s] First uptime from %s", managed_object.name, now) c.insert({ "object": oid, "start": now - delta, "stop": None, "last": now, "last_value": uptime })
class MACDB(Document): """ Customer MAC address database """ meta = { "collection": "noc.macs", "allow_inheritance": False, "indexes": ["mac", "interface"] } # Todo: Add Validation mac = StringField() vc_domain = ForeignKeyField(VCDomain, required=False) vlan = IntField() managed_object = ForeignKeyField(ManagedObject) interface = PlainReferenceField(Interface) last_changed = DateTimeField() def __unicode__(self): return self.mac def save(self): self.mac = MAC(self.mac) if not self.last_changed: self.last_changed = datetime.datetime.now() super(MACDB, self).save() @classmethod def submit(cls, mac, vc_domain, vlan, interface, timestamp=None): """ Submit mac to database Returns True if database been changed :param cls: :param mac: :param interface: :param timestamp: :return: """ if not timestamp: timestamp = datetime.datetime.now() managed_object = interface.managed_object mac = MAC(mac) vcd = vc_domain.id if vc_domain else None m = MACDB.objects.filter(mac=mac, vc_domain=vcd).first() if m: if (managed_object != m.managed_object or interface != m.interface or vlan != m.vlan): # Database change, write history MACLog(timestamp=m.last_changed, mac=mac, vc_domain_name=vc_domain.name if vc_domain else None, vlan=m.vlan, managed_object_name=m.managed_object.name, interface_name=m.interface.name).save() m.vlan = vlan m.managed_object = managed_object m.interface = interface m.last_changed = timestamp m.save() return True else: return False else: MACDB(mac=mac, vc_domain=vc_domain, vlan=vlan, managed_object=managed_object, interface=interface, last_changed=timestamp).save() return True