class TimePatternApplication(ExtModelApplication): """ TimePattern application """ title = _("Time Pattern") menu = [_("Setup"), _("Time Patterns")] model = TimePattern glyph = "clock-o" terms = ModelInline(TimePatternTerm) @view( url="^actions/test/", method=["POST"], access="read", api=True, validate={ "ids": ListOfParameter(element=ModelParameter(TimePattern)), "date": StringParameter(required=True), "time": StringParameter(required=True), }, ) def api_action_test(self, request, ids, date=None, time=None): d = "%sT%s" % (date, time) dt = datetime.datetime.strptime(d, "%Y-%m-%dT%H:%M") return { "ts": dt.isoformat(), "result": [{ "id": p.id, "name": p.name, "result": p.match(dt) } for p in ids], }
class VRFApplication(ExtModelApplication): """ VRF application """ title = _("VRF") menu = _("VRF") model = VRF query_fields = ["name", "rd", "description"] mrt_config = { "get_vrfs": { "map_script": "get_mpls_vpn", "access": "import" } } def field_row_class(self, o): return o.profile.style.css_class_name if o.profile.style else "" def clean(self, data): if not data.get("vpn_id"): vdata = {"type": "VRF", "name": data["name"], "rd": data.get("rd")} data["vpn_id"] = get_vpn_id(vdata) return super().clean(data) @view( url="^bulk/import/$", method=["POST"], access="import", api=True, validate={ "items": ListOfParameter(element=DictParameter( attrs={ "name": StringParameter(), "rd": RDParameter(), "vrf_group": ModelParameter(model=VRFGroup), "afi_ipv4": BooleanParameter(default=False), "afi_ipv6": BooleanParameter(default=False), "description": StringParameter(required=False), })) }, ) def api_bulk_import(self, request, items): n = 0 for i in items: if not VRF.objects.filter(name=i["name"], rd=i["rd"]).exists(): # Add only new VRF( name=i["name"], vrf_group=i["vrf_group"], rd=i["rd"], afi_ipv4=i["afi_ipv4"], afi_ipv6=i["afi_ipv6"], description=i.get("description"), ).save() n += 1 return {"status": True, "imported": n}
def init_plugin(self): super(DataPlugin, self).init_plugin() self.add_view("api_plugin_%s_save_data" % self.name, self.api_save_data, url="^(?P<id>[0-9a-f]{24})/plugin/data/$", method=["PUT"], validate={ "interface": StringParameter(), "key": StringParameter(), "value": UnicodeParameter() })
def init_plugin(self): super().init_plugin() self.add_view( "api_plugin_%s_get_layer" % self.name, self.api_get_layer, url=r"^plugin/%s/layers/(?P<layer>\S+)/$" % self.name, method=["GET"], ) self.add_view( "api_plugin_%s_object_data" % self.name, self.api_object_data, url="^(?P<id>[0-9a-f]{24})/plugin/%s/object_data/$" % self.name, method=["GET"], ) self.add_view( "api_plugin_%s_set_geopoint" % self.name, self.api_set_geopoint, url="^(?P<id>[0-9a-f]{24})/plugin/%s/set_geopoint/$" % self.name, method=["POST"], validate={ "srid": StringParameter(), "x": FloatParameter(), "y": FloatParameter() }, ) self.add_view( "api_plugin_%s_set_layer_visibility" % self.name, self.api_set_layer_visibility, url="^plugin/%s/layer_visibility/$" % self.name, method=["POST"], validate={ "layer": StringParameter(), "status": BooleanParameter() }, ) self.add_view( "api_plugin_%s_create" % self.name, self.api_create, url="^plugin/%s/$" % self.name, method=["POST"], validate={ "model": DocumentParameter(ObjectModel), "name": UnicodeParameter(), "srid": StringParameter(), "x": FloatParameter(), "y": FloatParameter(), }, )
class APITokenApplication(ExtApplication): """ APIToken Application """ @view("^(?P<type>[^/]+)/$", method=["GET"], access=PermitLogged(), api=True) def api_get_token(self, request, type): token = APIToken.objects.filter(type=type, user=request.user.id).first() if token: return {"type": token.type, "token": token.token} else: self.response_not_found() @view( "^(?P<type>[^/]+)/$", method=["POST"], access=PermitLogged(), validate={"token": StringParameter()}, api=True, ) def api_set_token(self, request, type, token=None): APIToken._get_collection().update( { "type": type, "user": request.user.id }, {"$set": { "token": token }}, upsert=True)
class JSONImportApplication(ExtApplication): """ main.jsonimport application """ title = "JSON Import" menu = "Setup | JSON Import" @view(url="^$", method=["POST"], access="launch", validate={"json": StringParameter(required=True)}, api=True) def api_import(self, request, json): try: jdata = json_decode(json) except Exception, why: return {"status": False, "error": "Invalid JSON: %s" % why} try: if isinstance(jdata, list): for d in jdata: self.import_object(d) else: self.import_object(jdata) except ValueError, why: return {"status": False, "error": str(why)}
class JSONImportApplication(ExtApplication): """ main.jsonimport application """ title = _("JSON Import") menu = [_("Setup"), _("JSON Import")] @view( url="^$", method=["POST"], access="launch", validate={"json": StringParameter(required=True)}, api=True, ) def api_import(self, request, json): try: jdata = ujson.loads(json) except Exception as e: return {"status": False, "error": "Invalid JSON: %s" % e} try: if isinstance(jdata, list): for d in jdata: Collection.install(d) c = Collection(d["$collection"]) c.update_item(d) else: Collection.install(jdata) c = Collection(jdata["$collection"]) c.update_item(jdata) except ValueError as e: return {"status": False, "error": str(e)} return {"status": True}
def test_dict_parameter(): assert DictParameter(attrs={ "i": IntParameter(), "s": StringParameter() }).clean({ "i": 10, "s": "ten" }) == { "i": 10, "s": "ten" } with pytest.raises(InterfaceTypeError): DictParameter(attrs={ "i": IntParameter(), "s": StringParameter() }).clean({ "i": "10", "x": "ten" })
def test_string_parameter(): assert StringParameter().clean("Test") == "Test" assert StringParameter().clean(10) == "10" assert StringParameter().clean(None) == "None" assert StringParameter(default="test").clean("no test") == "no test" assert StringParameter(default="test").clean(None) == "test" assert StringParameter(choices=["1", "2"]).clean("1") == "1" with pytest.raises(InterfaceTypeError): assert StringParameter(choices=["1", "2"]).clean("3")
def test_listof_parameter(): assert ListOfParameter(element=IntParameter()).clean([1, 2, 3]) == [1, 2, 3] assert ListOfParameter(element=IntParameter()).clean([1, 2, "3"]) == [1, 2, 3] with pytest.raises(InterfaceTypeError): ListOfParameter(element=IntParameter()).clean([1, 2, "x"]) assert ListOfParameter(element=StringParameter()).clean( [1, 2, 3, "x"]) == ["1", "2", "3", "x"] assert ListOfParameter(element=StringParameter(), default=[]).clean(None) == [] assert ListOfParameter(element=StringParameter(), default=[1, 2, 3]).clean(None) == ["1", "2", "3"] assert ListOfParameter( element=[StringParameter(), IntParameter()]).clean([ ("a", 1), ("b", "2") ]) == [["a", 1], ["b", 2]] with pytest.raises(InterfaceTypeError): assert ListOfParameter( element=[StringParameter(), IntParameter()]).clean([("a", 1), ("b", "x")])
def test_dictlist_parameter(): assert DictListParameter().clean([{ "1": 2 }, { "2": 3, "4": 1 }]) == [{ "1": 2 }, { "2": 3, "4": 1 }] assert DictListParameter(attrs={ "i": IntParameter(), "s": StringParameter() }).clean([{ "i": 10, "s": "ten" }, { "i": "5", "s": "five" }]) == [{ "i": 10, "s": "ten" }, { "i": 5, "s": "five" }] assert DictListParameter(attrs={ "i": IntParameter(), "s": StringParameter() }, convert=True).clean({ "i": "10", "s": "ten" }) == [{ "i": 10, "s": "ten" }]
class AccountApplication(ExtApplication): """ support.account application """ title = _("Account") menu = [_("Setup"), _("Account")] @view(url="^$", method=["GET"], access="launch", api=True) def api_get(self, request): c = CPClient() data = {} if c.has_account(): data["account"] = c.account_info() for i in data["account"].get("industries", []): data["account"]["ind_%s" % i] = True if c.has_system(): data["system"] = c.system_info() return data @view(url="^account/attach/$", method=["POST"], access="launch", api=True, validate={ "name": StringParameter(), "password": StringParameter(required=False) }) def api_attach_account(self, request, name, password): c = CPClient() if c.has_account(): self.response_forbidden() try: c.attach_account(name, password) except CPClient.Error, why: return {"status": False, "message": str(why)} return {"status": True, "message": "Ok"}
def test_string_parameter(raw, config, expected): assert StringParameter(**config).clean(raw) == expected
def test_subclassof_parameter_error(raw, config): with pytest.raises(InterfaceTypeError): assert SubclassOfParameter(**config).clean(raw) @pytest.mark.parametrize( "raw,config,expected", [ ([1, 2, 3], { "element": IntParameter() }, [1, 2, 3]), ([1, 2, "3"], { "element": IntParameter() }, [1, 2, 3]), ([1, 2, 3, "x"], { "element": StringParameter() }, ["1", "2", "3", "x"]), (None, { "element": StringParameter(), "default": [] }, []), (None, { "element": StringParameter(), "default": [1, 2, 3] }, ["1", "2", "3"]), ( [("a", 1), ("b", "2")], { "element": [StringParameter(), IntParameter()] },
from noc.core.topology.constraint.base import BaseConstraint from noc.core.topology.constraint.vlan import VLANConstraint from noc.core.topology.constraint.upwards import UpwardsConstraint from noc.core.topology.constraint.any import AnyConstraint from noc.core.topology.goal.base import BaseGoal from noc.core.topology.goal.managedobject import ManagedObjectGoal from noc.core.topology.goal.level import ManagedObjectLevelGoal from noc.core.text import split_alnum from ..base import NBIAPI # Constants MAX_DEPTH_DEFAULT = 20 N_SHORTEST_DEFAULT = 10 # id/remote system pointer PointerId = DictParameter(attrs={"id": StringParameter()}) PointerRemote = DictParameter(attrs={ "remote_system": StringParameter(), "remote_id": StringParameter() }) Pointer = PointerId | PointerRemote # from: section ObjectPointer = DictParameter( attrs={ "object": Pointer, "interface": DictParameter(attrs={"name": StringParameter()}, required=False), }) InterfacePointer = DictParameter(
class ManagedObjectApplication(ExtModelApplication): """ ManagedObject application """ title = _("Managed Objects") menu = _("Managed Objects") model = ManagedObject query_condition = "icontains" query_fields = ["name", "description"] secret_fields = {"password", "super_password", "snmp_ro", "snmp_rw"} # Inlines attrs = ModelInline(ManagedObjectAttribute) cfg = RepoInline("config", access="config") extra_permissions = ["alarm", "change_interface"] implied_permissions = { "read": ["inv:networksegment:lookup", "main:handler:lookup"] } diverged_permissions = {"config": "read", "console": "script"} order_map = { "address": " cast_test_to_inet(address) ", "-address": " cast_test_to_inet(address) ", "profile": "CASE %s END" % " ".join([ "WHEN %s='%s' THEN %s" % ("profile", pk, i) for i, pk in enumerate( Profile.objects.filter().order_by("name").values_list("id")) ]), "-profile": "CASE %s END" % " ".join([ "WHEN %s='%s' THEN %s" % ("profile", pk, i) for i, pk in enumerate( Profile.objects.filter().order_by("-name").values_list("id")) ]), "platform": "CASE %s END" % " ".join([ "WHEN %s='%s' THEN %s" % ("platform", pk, i) for i, pk in enumerate(Platform.objects.filter().order_by( "name").values_list("id")) ]), "-platform": "CASE %s END" % " ".join([ "WHEN %s='%s' THEN %s" % ("platform", pk, i) for i, pk in enumerate(Platform.objects.filter().order_by( "-name").values_list("id")) ]), "version": "CASE %s END" % " ".join([ "WHEN %s='%s' THEN %s" % ("version", pk, i) for i, pk in enumerate(Firmware.objects.filter().order_by( "version").values_list("id")) ]), "-version": "CASE %s END" % " ".join([ "WHEN %s='%s' THEN %s" % ("version", pk, i) for i, pk in enumerate(Firmware.objects.filter().order_by( "-version").values_list("id")) ]), } resource_group_fields = [ "static_service_groups", "effective_service_groups", "static_client_groups", "effective_client_groups", ] DISCOVERY_JOBS = [ ("box", "noc.services.discovery.jobs.box.job.BoxDiscoveryJob"), ("periodic", "noc.services.discovery.jobs.periodic.job.PeriodicDiscoveryJob"), ] def field_row_class(self, o): return o.object_profile.style.css_class_name if o.object_profile.style else "" def bulk_field_interface_count(self, data): """ Apply interface_count fields :param data: :return: """ mo_ids = [x["id"] for x in data] if not mo_ids: return data # Collect interface counts r = Interface._get_collection().aggregate([ { "$match": { "managed_object": { "$in": mo_ids }, "type": "physical" } }, { "$group": { "_id": "$managed_object", "total": { "$sum": 1 } } }, ]) ifcount = dict((x["_id"], x["total"]) for x in r) # Apply interface counts for x in data: x["interface_count"] = ifcount.get(x["id"]) or 0 return data def bulk_field_link_count(self, data): """ Apply link_count fields :param data: :return: """ mo_ids = [x["id"] for x in data] if not mo_ids: return data # Collect interface counts r = Link._get_collection().aggregate([ { "$match": { "linked_objects": { "$in": mo_ids } } }, { "$unwind": "$linked_objects" }, { "$group": { "_id": "$linked_objects", "total": { "$sum": 1 } } }, ]) links_count = dict((x["_id"], x["total"]) for x in r) # Apply interface counts for x in data: x["link_count"] = links_count.get(x["id"]) or 0 return data def instance_to_dict(self, o, fields=None): def sg_to_list(items): return [{ "group": x, "group__label": unicode(ResourceGroup.get_by_id(x)) } for x in items] data = super(ManagedObjectApplication, self).instance_to_dict(o, fields) # Expand resource groups fields for fn in self.resource_group_fields: data[fn] = sg_to_list(data.get(fn) or []) return data def clean(self, data): # Clean resource groups for fn in self.resource_group_fields: if fn.startswith("effective_") and fn in data: del data[fn] continue data[fn] = [x["group"] for x in (data.get(fn) or [])] # Clean other return super(ManagedObjectApplication, self).clean(data) def cleaned_query(self, q): if "administrative_domain" in q: ad = AdministrativeDomain.get_nested_ids( int(q["administrative_domain"])) if ad: del q["administrative_domain"] else: ad = None if "selector" in q: s = self.get_object_or_404(ManagedObjectSelector, id=int(q["selector"])) del q["selector"] else: s = None r = super(ManagedObjectApplication, self).cleaned_query(q) if s: r["id__in"] = ManagedObject.objects.filter(s.Q) if ad: r["administrative_domain__in"] = ad return r def get_Q(self, request, query): q = super(ManagedObjectApplication, self).get_Q(request, query) sq = ManagedObject.get_search_Q(query) if sq: q |= sq return q def queryset(self, request, query=None): qs = super(ManagedObjectApplication, self).queryset(request, query) if not request.user.is_superuser: qs = qs.filter(UserAccess.Q(request.user)) qs = qs.exclude(name__startswith="wiping-") return qs @view(url=r"^(?P<id>\d+)/links/$", method=["GET"], access="read", api=True) def api_links(self, request, id): o = self.get_object_or_404(ManagedObject, id=id) if not o.has_access(request.user): return self.response_forbidden("Access denied") # Get links result = [] for link in Link.object_links(o): ifaces = [] r = [] for i in link.interfaces: if i.managed_object.id == o.id: ifaces += [i] else: r += [i] for li, ri in zip(ifaces, r): result += [{ "link_id": str(link.id), "local_interface": str(li.id), "local_interface__label": li.name, "remote_object": ri.managed_object.id, "remote_object__label": ri.managed_object.name, "remote_platform": ri.managed_object.platform.name if ri.managed_object.platform else "", "remote_interface": str(ri.id), "remote_interface__label": ri.name, "discovery_method": link.discovery_method, "local_description": li.description, "remote_description": ri.description, "first_discovered": link.first_discovered.isoformat() if link.first_discovered else None, "last_seen": link.last_seen.isoformat() if link.last_seen else None, }] return result @view(url=r"^(?P<id>\d+)/discovery/$", method=["GET"], access="read", api=True) def api_discovery(self, request, id): from noc.core.scheduler.job import Job o = self.get_object_or_404(ManagedObject, id=id) if not o.has_access(request.user): return self.response_forbidden("Access denied") link_count = defaultdict(int) for link in Link.object_links(o): m = link.discovery_method or "" if "+" in m: m = m.split("+")[0] link_count[m] += 1 r = [{ "name": "ping", "enable_profile": o.object_profile.enable_ping, "status": o.get_status(), "last_run": None, "last_status": None, "next_run": None, "jcls": None, }] for name, jcls in self.DISCOVERY_JOBS: job = Job.get_job_data( "discovery", jcls=jcls, key=o.id, pool=o.pool.name) or {} d = { "name": name, "enable_profile": getattr(o.object_profile, "enable_%s_discovery" % name), "status": job.get(Job.ATTR_STATUS), "last_run": self.to_json(job.get(Job.ATTR_LAST)), "last_status": job.get(Job.ATTR_LAST_STATUS), "next_run": self.to_json(job.get(Job.ATTR_TS)), "jcls": jcls, } r += [d] return r @view( url=r"^actions/set_managed/$", method=["POST"], access="create", api=True, validate={ "ids": ListOfParameter(element=ModelParameter(ManagedObject), convert=True) }, ) def api_action_set_managed(self, request, ids): for o in ids: if not o.has_access(request.user): continue o.is_managed = True o.save() return "Selected objects set to managed state" @view( url=r"^actions/set_unmanaged/$", method=["POST"], access="create", api=True, validate={ "ids": ListOfParameter(element=ModelParameter(ManagedObject), convert=True) }, ) def api_action_set_unmanaged(self, request, ids): for o in ids: if not o.has_access(request.user): continue o.is_managed = False o.save() return "Selected objects set to unmanaged state" @view(url=r"^(?P<id>\d+)/discovery/run/$", method=["POST"], access="change_discovery", api=True) def api_run_discovery(self, request, id): o = self.get_object_or_404(ManagedObject, id=id) if not o.has_access(request.user): return self.response_forbidden("Access denied") r = ujson.loads(request.body).get("names", []) for name, jcls in self.DISCOVERY_JOBS: if name not in r: continue if not getattr(o.object_profile, "enable_%s_discovery" % name): continue # Disabled by profile Job.submit("discovery", jcls, key=o.id, pool=o.pool.name) return {"success": True} @view(url=r"^(?P<id>\d+)/discovery/stop/$", method=["POST"], access="change_discovery", api=True) def api_stop_discovery(self, request, id): o = self.get_object_or_404(ManagedObject, id=id) if not o.has_access(request.user): return self.response_forbidden("Access denied") r = ujson.loads(request.body).get("names", []) for name, jcls in self.DISCOVERY_JOBS: if name not in r: continue if not getattr(o.object_profile, "enable_%s_discovery" % name): continue # Disabled by profile Job.remove("discovery", jcls, key=o.id, pool=o.pool.name) return {"success": True} @view(url=r"^(?P<id>\d+)/interface/$", method=["GET"], access="read", api=True) def api_interface(self, request, id): """ GET interfaces :param managed_object: :return: """ def sorted_iname(s): return sorted(s, key=lambda x: split_alnum(x["name"])) def get_style(i): profile = i.profile if profile: try: return style_cache[profile.id] except KeyError: pass if profile.style: s = profile.style.css_class_name else: s = "" style_cache[profile.id] = s return s else: return "" def get_link(i): link = i.link if not link: return None if link.is_ptp: # ptp o = link.other_ptp(i) label = "%s:%s" % (o.managed_object.name, o.name) elif link.is_lag: # unresolved LAG o = [ ii for ii in link.other(i) if ii.managed_object.id != i.managed_object.id ] label = "LAG %s: %s" % (o[0].managed_object.name, ", ".join( ii.name for ii in o)) else: # Broadcast label = ", ".join("%s:%s" % (ii.managed_object.name, ii.name) for ii in link.other(i)) return {"id": str(link.id), "label": label} # Get object o = self.get_object_or_404(ManagedObject, id=int(id)) if not o.has_access(request.user): return self.response_forbidden("Permission denied") # Physical interfaces # @todo: proper ordering default_state = ResourceState.get_default() style_cache = {} # profile_id -> css_style l1 = [{ "id": str(i.id), "name": i.name, "description": i.description, "status": i.status, "mac": i.mac, "ifindex": i.ifindex, "lag": (i.aggregated_interface.name if i.aggregated_interface else ""), "link": get_link(i), "profile": str(i.profile.id) if i.profile else None, "profile__label": unicode(i.profile) if i.profile else None, "enabled_protocols": i.enabled_protocols, "project": i.project.id if i.project else None, "project__label": unicode(i.project) if i.project else None, "state": i.state.id if i.state else default_state.id, "state__label": unicode(i.state if i.state else default_state), "vc_domain": i.vc_domain.id if i.vc_domain else None, "vc_domain__label": unicode(i.vc_domain) if i.vc_domain else None, "row_class": get_style(i), } for i in Interface.objects.filter(managed_object=o.id, type="physical")] # LAG lag = [{ "id": str(i.id), "name": i.name, "description": i.description, "profile": str(i.profile.id) if i.profile else None, "profile__label": unicode(i.profile) if i.profile else None, "members": [ j.name for j in Interface.objects.filter(managed_object=o.id, aggregated_interface=i.id) ], "row_class": get_style(i), } for i in Interface.objects.filter(managed_object=o.id, type="aggregated")] # L2 interfaces l2 = [{ "name": i.name, "description": i.description, "untagged_vlan": i.untagged_vlan, "tagged_vlans": i.tagged_vlans, } for i in SubInterface.objects.filter(managed_object=o.id, enabled_afi="BRIDGE")] # L3 interfaces q = MQ(enabled_afi="IPv4") | MQ(enabled_afi="IPv6") l3 = [{ "name": i.name, "description": i.description, "ipv4_addresses": i.ipv4_addresses, "ipv6_addresses": i.ipv6_addresses, "enabled_protocols": i.enabled_protocols, "vlan": i.vlan_ids, "vrf": i.forwarding_instance.name if i.forwarding_instance else "", "mac": i.mac, } for i in SubInterface.objects.filter(managed_object=o.id).filter(q)] return { "l1": sorted_iname(l1), "lag": sorted_iname(lag), "l2": sorted_iname(l2), "l3": sorted_iname(l3), } @view(url=r"^(?P<id>\d+)/interface/$", method=["POST"], access="change_interface", api=True) def api_set_interface(self, request, id): def get_or_none(c, v): if not v: return None return c.objects.get(id=v) o = self.get_object_or_404(ManagedObject, id=int(id)) if not o.has_access(request.user): return self.response_forbidden("Access denied") d = ujson.loads(request.body) if "id" in d: i = self.get_object_or_404(Interface, id=d["id"]) if i.managed_object.id != o.id: return self.response_not_found() # Set profile if "profile" in d: p = get_or_none(InterfaceProfile, d["profile"]) i.profile = p if p: i.profile_locked = True # Project if "project" in d: i.project = get_or_none(Project, d["project"]) # State if "state" in d: i.state = get_or_none(ResourceState, d["state"]) # VC Domain if "vc_domain" in d: i.vc_domain = get_or_none(VCDomain, d["vc_domain"]) # i.save() return {"success": True} @view(method=["DELETE"], url=r"^(?P<id>\d+)/?$", access="delete", api=True) def api_delete(self, request, id): """ Override default method :param request: :param id: :return: """ try: o = self.queryset(request).get(id=int(id)) except self.model.DoesNotExist: return self.render_json({ "status": False, "message": "Not found" }, status=self.NOT_FOUND) if not o.has_access(request.user): return self.response_forbidden("Access denied") # Run sa.wipe_managed_object job instead o.name = "wiping-%d" % o.id o.is_managed = False o.description = "Wiping! Do not touch!" o.save() call_later("noc.sa.wipe.managedobject.wipe", o=o.id) return HttpResponse(status=self.DELETED) @view( url=r"^actions/run_discovery/$", method=["POST"], access="launch", api=True, validate={ "ids": ListOfParameter(element=ModelParameter(ManagedObject), convert=True) }, ) def api_action_run_discovery(self, request, ids): d = 0 for o in ids: if not o.has_access(request.user): continue o.run_discovery(delta=d) d += 1 return "Discovery processes has been scheduled" def get_nested_inventory(self, o): rev = o.get_data("asset", "revision") if rev == "None": rev = "" r = { "id": str(o.id), "serial": o.get_data("asset", "serial"), "revision": rev or "", "description": o.model.description, "model": o.model.name, } children = [] for n in o.model.connections: if n.direction == "i": c, r_object, _ = o.get_p2p_connection(n.name) if c is None: children += [{ "id": None, "name": n.name, "leaf": True, "serial": None, "description": "--- EMPTY ---", "model": None, }] else: cc = self.get_nested_inventory(r_object) cc["name"] = n.name children += [cc] elif n.direction == "s": children += [{ "id": None, "name": n.name, "leaf": True, "serial": None, "description": n.description, "model": ", ".join(n.protocols), }] if children: to_expand = "Transceiver" not in o.model.name r["children"] = children r["expanded"] = to_expand else: r["leaf"] = True return r @view(url=r"^(?P<id>\d+)/inventory/$", method=["GET"], access="read", api=True) def api_inventory(self, request, id): o = self.get_object_or_404(ManagedObject, id=id) if not o.has_access(request.user): return self.response_forbidden("Access denied") r = [] for p in o.get_inventory(): c = self.get_nested_inventory(p) c["name"] = p.name or o.name r += [c] return {"expanded": True, "children": r} @view(url=r"^(?P<id>\d+)/confdb/$", method=["GET"], access="config", api=True) def api_confdb(self, request, id): o = self.get_object_or_404(ManagedObject, id=id) if not o.has_access(request.user): return self.response_forbidden("Access denied") cleanup = True if "cleanup" in request.GET: c = request.GET["cleanup"].strip().lower() cleanup = c not in ("no", "false", "0") cdb = o.get_confdb(cleanup=cleanup) return self.render_plain_text(cdb.dump("json"), content_type="text/json") @view( url=r"^(?P<id>\d+)/confdb/$", method=["POST"], validate={ "query": StringParameter(), "cleanup": BooleanParameter(default=True), "dump": BooleanParameter(default=False), }, access="config", api=True, ) def api_confdb_query(self, request, id, query="", cleanup=True, dump=False): o = self.get_object_or_404(ManagedObject, id=id) if not o.has_access(request.user): return self.response_forbidden("Access denied") cdb = o.get_confdb(cleanup=cleanup) try: r = list(cdb.query(query)) result = {"status": True, "result": r} if dump: result["confdb"] = ujson.loads(cdb.dump("json")) except SyntaxError as e: result = {"status": False, "error": str(e)} return result @view(url=r"^(?P<id>\d+)/job_log/(?P<job>\S+)/$", method=["GET"], access="read", api=True) def api_job_log(self, request, id, job): o = self.get_object_or_404(ManagedObject, id=id) if not o.has_access(request.user): return self.response_forbidden("Access denied") # fs = gridfs.GridFS(get_db(), "noc.joblog") key = "discovery-%s-%s" % (job, o.id) d = get_db()["noc.joblog"].find_one({"_id": key}) if d and d["log"]: return self.render_plain_text(zlib.decompress(str(d["log"]))) else: return self.render_plain_text("No data") @view(url=r"^(?P<id>\d+)/interactions/$", method=["GET"], access="interactions", api=True) def api_interactions(self, request, id): o = self.get_object_or_404(ManagedObject, id=id) if not o.has_access(request.user): return self.response_forbidden("Access denied") return [{ "ts": self.to_json(i.timestamp), "op": i.op, "user": i.user, "text": i.text } for i in InteractionLog.objects.filter( object=o.id).order_by("-timestamp")] @view(url=r"^(?P<id>\d+)/scripts/$", method=["GET"], access="script", api=True) def api_scripts(self, request, id): o = self.get_object_or_404(ManagedObject, id=id) if not o.has_access(request.user): return self.response_forbidden("Access denied") r = [] for s in o.scripts: sn = o.profile.name + "." + s script = script_loader.get_script(sn) if not script: self.logger.error("Failed to load script: %s", sn) continue interface = script.interface() ss = { "name": s, "has_input": any(interface.gen_parameters()), "require_input": interface.has_required_params, "form": interface.get_form(), "preview": interface.preview or "NOC.sa.managedobject.scripts.JSONPreview", } r += [ss] return r @view(url=r"^(?P<id>\d+)/scripts/(?P<name>[^/]+)/$", method=["POST"], access="script", api=True) def api_run_script(self, request, id, name): o = self.get_object_or_404(ManagedObject, id=id) if not o.has_access(request.user): return {"error": "Access denied"} if name not in o.scripts: return {"error": "Script not found: %s" % name} params = self.deserialize(request.body) try: result = o.scripts[name](**params) except Exception as e: return {"error": str(e)} return {"result": result} @view(url=r"^(?P<id>\d+)/console/$", method=["POST"], access="console", api=True) def api_console_command(self, request, id): o = self.get_object_or_404(ManagedObject, id=id) if not o.has_access(request.user): return {"error": "Access denied"} if "commands" not in o.scripts: return {"error": "Script not found: commands"} params = self.deserialize(request.body) try: result = o.scripts.commands(**params) except Exception as e: return {"error": str(e)} return {"result": result} @view(url=r"(?P<id>\d+)/caps/$", method=["GET"], access="read", api=True) def api_get_caps(self, request, id): o = self.get_object_or_404(ManagedObject, id=id) if not o.has_access(request.user): return self.response_forbidden("Access denied") r = [] oc = ObjectCapabilities.objects.filter(object=o).first() if oc: for c in oc.caps: r += [{ "capability": c.capability.name, "description": c.capability.description, "type": c.capability.type, "value": c.value, "source": c.source, }] return sorted(r, key=lambda x: x["capability"]) @view(url=r"(?P<id>\d+)/facts/$", method=["GET"], access="read", api=True) def api_get_facts(self, request, id): o = self.get_object_or_404(ManagedObject, id=id) if not o.has_access(request.user): return self.response_forbidden("Access denied") return sorted( ({ "cls": f.cls, "label": f.label, "attrs": [{ "name": a, "value": f.attrs[a] } for a in f.attrs], "introduced": f.introduced.isoformat(), "changed": f.changed.isoformat(), } for f in ObjectFact.objects.filter(object=o.id)), key=lambda x: (x["cls"], x["label"]), ) @view(url=r"(?P<id>\d+)/revalidate/$", method=["POST"], access="read", api=True) def api_revalidate(self, request, id): def revalidate(o): engine = Engine(o) engine.check() return self.response({"status": True}, self.OK) o = self.get_object_or_404(ManagedObject, id=id) if not o.has_access(request.user): return self.response_forbidden("Access denied") return self.submit_slow_op(request, revalidate, o) @view(url=r"(?P<id>\d+)/actions/(?P<action>\S+)/$", method=["POST"], access="action", api=True) def api_action(self, request, id, action): def execute(o, a, args): return a.execute(o, **args) o = self.get_object_or_404(ManagedObject, id=id) if not o.has_access(request.user): return self.response_forbidden("Access denied") a = self.get_object_or_404(Action, name=action) # @todo: Check access body = request.body if body: args = ujson.loads(body) else: args = {} return self.submit_slow_op(request, execute, o, a, args) @view(url=r"^link/fix/(?P<link_id>[0-9a-f]{24})/$", method=["POST"], access="change_link") def api_fix_links(self, request, link_id): def get_mac(arp, ip): for r in arp: if r["ip"] == ip: return r["mac"] return None def get_interface(macs, mac): for m in macs: if m["mac"] == mac: return m["interfaces"][0] return None def error_status(message, *args): self.logger.error(message, *args) return {"status": False, "message": message % args} def success_status(message, *args): self.logger.error(message, *args) return {"status": True, "message": message % args} link = self.get_object_or_404(Link, id=link_id) if len(link.interfaces) != 2: return error_status("Cannot fix link: Not P2P") mo1 = link.interfaces[0].managed_object mo2 = link.interfaces[1].managed_object if mo1.id == mo2.id: return error_status("Cannot fix circular links") # Ping each other self.logger.info("[%s] Pinging %s", mo1.name, mo2.address) r1 = mo1.scripts.ping(address=mo2.address) if not r1["success"]: return error_status("Failed to ping %s", mo2.name) self.logger.info("[%s] Pinging %s", mo2.name, mo1.address) r2 = mo2.scripts.ping(address=mo1.address) if not r2["success"]: return error_status("Failed to ping %s", mo1.name) # Get ARPs mac2 = get_mac(mo1.scripts.get_arp(), mo2.address) if not mac2: return error_status("[%s] ARP cache is not filled properly", mo1.name) self.logger.info("[%s] MAC=%s", mo2.name, mac2) mac1 = get_mac(mo2.scripts.get_arp(), mo1.address) if not mac1: return error_status("[%s] ARP cache is not filled properly", mo2.name) self.logger.info("[%s] MAC=%s", mo1.name, mac1) # Get MACs r1 = mo1.scripts.get_mac_address_table(mac=mac2) self.logger.info("[%s] MACS=%s", mo1.name, r1) r2 = mo2.scripts.get_mac_address_table(mac=mac1) self.logger.info("[%s] MACS=%s", mo2.name, r2) # mo1: Find mo2 i1 = get_interface(r1, mac2) if not i1: return error_status("[%s] Cannot find %s in the MAC address table", mo1.name, mo2.name) # mo2: Find mo1 i2 = get_interface(r2, mac1) if not i1: return error_status("[%s] Cannot find %s in the MAC address table", mo2.name, mo1.name) self.logger.info("%s:%s -- %s:%s", mo1.name, i1, mo2.name, i2) if link.interfaces[0].name == i1 and link.interfaces[1].name == i2: return success_status("Linked properly") # Get interfaces iface1 = mo1.get_interface(i1) if not iface1: return error_status("[%s] Interface not found: %s", mo1.name, i1) iface2 = mo2.get_interface(i2) if not iface2: return error_status("[%s] Interface not found: %s", mo2.name, i2) # Check we can relink if_ids = [i.id for i in link.interfaces] if iface1.id not in if_ids and iface1.is_linked: return error_status("[%s] %s is already linked", mo1.name, i1) if iface2.id not in if_ids and iface2.is_linked: return error_status("[%s] %s is already linked", mo2.name, i2) # Relink self.logger.info("Relinking") link.delete() iface1.link_ptp(iface2, method="macfix") return success_status("Relinked") @view(url=r"^(?P<id>\d+)/cpe/$", method=["GET"], access="read", api=True) def api_cpe(self, request, id): """ GET CPEs :param request: :param id: :return: """ def sorted_iname(s): return sorted(s, key=lambda x: split_alnum(x["name"])) # Get object o = self.get_object_or_404(ManagedObject, id=int(id)) if not o.has_access(request.user): return self.response_forbidden("Permission denied") # CPE # @todo: proper ordering # default_state = ResourceState.get_default() # style_cache = {} # profile_id -> css_style l1 = [ { "global_id": str(c.global_id), "name": c.name or "", "interface": c.interface, "local_id": c.local_id, "serial": c.serial or "", "status": c.status, "description": c.description or "", "address": c.ip or "", "model": c.model or "", "version": c.version or "", "mac": c.mac or "", "location": c.location or "", "distance": str(c.distance) # "row_class": get_style(i) } for c in CPEStatus.objects.filter(managed_object=o.id) ] return {"cpe": sorted_iname(l1)}
class ReportInterfaceStatusApplication(ExtApplication): menu = _("Reports") + "|" + _("Interface Status") title = _("Interface Status") @view( "^download/$", method=["GET"], access="launch", api=True, validate={ "administrative_domain": StringParameter(required=False), "interface_profile": StringParameter(required=False), "selector": StringParameter(required=False), "zero": StringParameter(required=False), "def_profile": StringParameter(required=False), "columns": StringParameter(required=False), "o_format": StringParameter(choices=["csv", "xlsx"]), }, ) def api_report( self, request, o_format, administrative_domain=None, selector=None, interface_profile=None, zero=None, def_profile=None, columns=None, enable_autowidth=False, ): def humanize_speed(speed): if not speed: return "-" for t, n in [(1000000, "G"), (1000, "M"), (1, "k")]: if speed >= t: if speed // t * t == speed: return "%d%s" % (speed // t, n) else: return "%.2f%s" % (float(speed) / t, n) return str(speed) def row(row): def qe(v): if v is None: return "" if isinstance(v, unicode): return v.encode("utf-8") elif isinstance(v, datetime.datetime): return v.strftime("%Y-%m-%d %H:%M:%S") elif not isinstance(v, str): return str(v) else: return v return [qe(x) for x in row] def translate_row(row, cmap): return [row[i] for i in cmap] cols = [ "object_name", "object_address", "object_model", "object_software", "object_port_name", "object_port_profile_name", "object_port_status", "object_link_status", "object_port_speed", "object_port_duplex", "object_port_untagged_vlan", "object_port_tagged_vlans", ] header_row = [ "MANAGED_OBJECT", "OBJECT_ADDRESS", "OBJECT_MODEL", "OBJECT_SOFTWARE", "PORT_NAME", "PORT_PROFILE_NAME", "PORT_STATUS", "LINK_STATUS", "PORT_SPEED", "PORT_DUPLEX", "PORT_UNTAGGED_VLAN", "PORT_TAGGED_VLANS", ] if columns: cmap = [] for c in columns.split(","): try: cmap += [cols.index(c)] except ValueError: continue else: cmap = list(range(len(cols))) r = [translate_row(header_row, cmap)] mo = {} if_p = {} DUPLEX = {True: "Full", False: "Half"} for ifp in InterfaceProfile.objects.filter(): if_p[ifp.id] = {"name": ifp.name} mos = ManagedObject.objects.filter(is_managed=True) if (request.user.is_superuser and not administrative_domain and not selector and not interface_profile): mos = ManagedObject.objects.filter(is_managed=True) if not request.user.is_superuser: mos = mos.filter( administrative_domain__in=UserAccess.get_domains(request.user)) if administrative_domain: ads = AdministrativeDomain.get_nested_ids( int(administrative_domain)) mos = mos.filter(administrative_domain__in=ads) if selector: selector = ManagedObjectSelector.get_by_id(int(selector)) mos = mos.filter(selector.Q) for o in mos: mo[o.id] = { "type": "managedobject", "id": str(o.id), "name": o.name, "status": o.is_managed, "address": o.address, "vendor": o.vendor, "version": o.version, "platform": o.platform, } mos_id = list(mos.values_list("id", flat=True)) rld = ReportInterfaceStatus(mos_id, zero, def_profile, interface_profile) for i in rld.out: untag, tagged = "", "" if i["subs"]: untag = i["subs"][0].get("untagged_vlan", "") tagged = list_to_ranges(i["subs"][0].get("tagged_vlans", [])) r += [ translate_row( row([ mo[i["managed_object"]]["name"], mo[i["managed_object"]]["address"], "%s %s" % ( str(mo[i["managed_object"]]["vendor"]), str(mo[i["managed_object"]]["platform"]), ), str(mo[i["managed_object"]]["version"]), i["name"], if_p[i["profile"]]["name"], "UP" if i["admin_status"] is True else "Down", "UP" if "oper_status" in i and i["oper_status"] is True else "Down", humanize_speed(i["in_speed"]) if "in_speed" in i else "-", DUPLEX.get(i["full_duplex"]) if "full_duplex" in i and "in_speed" in i else "-", untag, tagged, ]), cmap, ) ] filename = "interface_status_report_%s" % datetime.datetime.now( ).strftime("%Y%m%d") if o_format == "csv": response = HttpResponse(content_type="text/csv") response[ "Content-Disposition"] = 'attachment; filename="%s.csv"' % filename writer = csv.writer(response, dialect="excel", delimiter=";") writer.writerows(r) return response elif o_format == "xlsx": response = StringIO() wb = xlsxwriter.Workbook(response) cf1 = wb.add_format({"bottom": 1, "left": 1, "right": 1, "top": 1}) ws = wb.add_worksheet("Objects") max_column_data_length = {} for rn, x in enumerate(r): for cn, c in enumerate(x): if rn and (r[0][cn] not in max_column_data_length or len(str(c)) > max_column_data_length[r[0][cn]]): max_column_data_length[r[0][cn]] = len(str(c)) ws.write(rn, cn, c, cf1) ws.autofilter(0, 0, rn, cn) ws.freeze_panes(1, 0) for cn, c in enumerate(r[0]): # Set column width width = get_column_width(c) if enable_autowidth and width < max_column_data_length[c]: width = max_column_data_length[c] ws.set_column(cn, cn, width=width) wb.close() response.seek(0) response = HttpResponse(response.getvalue(), content_type="application/vnd.ms-excel") # response = HttpResponse( # content_type="application/x-ms-excel") response[ "Content-Disposition"] = 'attachment; filename="%s.xlsx"' % filename response.close() return response
from noc.sa.interfaces.base import ( DictParameter, DictListParameter, StringParameter, StringListParameter, IntParameter, ListOfParameter, ListParameter, ) from noc.pm.models.metrictype import MetricType from noc.services.nbi.base import NBIAPI Request = DictParameter( attrs={ "host": StringParameter(required=True), "port": StringParameter(required=True), "sn": StringParameter(required=True), }) class GetllidzteAPI(NBIAPI): name = "getllidzte" @authenticated @tornado.gen.coroutine def post(self): connect() code, result = yield self.executor.submit(self.handler) self.set_status(code) if isinstance(result, six.string_types):
from noc.pm.models.metrictype import MetricType from noc.core.clickhouse.connect import ClickhouseClient from noc.core.clickhouse.error import ClickhouseError from noc.sa.models.profile import Profile from ..base import NBIAPI Request = DictParameter( attrs={ "from": DateTimeShiftParameter(required=True), "to": DateTimeShiftParameter(required=True), "metrics": DictListParameter( attrs={ "object": StringParameter(required=True), "interfaces": StringListParameter(required=False), "metric_types": StringListParameter(required=True), }, required=True, ), }) S_INTERFACE = "interface" class ObjectMetricsAPI(NBIAPI): name = "objectmetrics" @authenticated @tornado.gen.coroutine
from noc.sa.interfaces.base import ( DictParameter, DictListParameter, StringParameter, StringListParameter, IntParameter, ListOfParameter, ListParameter, ) from noc.pm.models.metrictype import MetricType from noc.services.nbi.base import NBIAPI Request = DictParameter( attrs={ "org": StringParameter(required=True), } ) class GetoltAPI(NBIAPI): name = "getolt" @authenticated @tornado.gen.coroutine def post(self): connect() code, result = yield self.executor.submit(self.handler) self.set_status(code) if isinstance(result, six.string_types): self.write(result)
def test_string_parameter_error(raw, config): with pytest.raises(InterfaceTypeError): assert StringParameter(**config).clean(raw)
class AlarmApplication(ExtApplication): """ fm.alarm application """ title = _("Alarm") menu = _("Alarms") glyph = "exclamation-triangle" implied_permissions = {"launch": ["sa:managedobject:alarm"]} model_map = {"A": ActiveAlarm, "C": ArchivedAlarm} clean_fields = { "managed_object": ModelParameter(ManagedObject), "timestamp": DateTimeParameter() } ignored_params = ["status", "_dc"] diagnostic_plugin = AlarmPlugin(name="diagnostic", config={}) DEFAULT_ARCH_ALARM = datetime.timedelta( seconds=config.web.api_arch_alarm_limit) def __init__(self, *args, **kwargs): ExtApplication.__init__(self, *args, **kwargs) from .plugins.base import AlarmPlugin # Load plugins self.plugins = {} for f in os.listdir("services/web/apps/fm/alarm/plugins/"): if (not f.endswith(".py") or f == "base.py" or f.startswith("_")): continue mn = "noc.services.web.apps.fm.alarm.plugins.%s" % f[:-3] m = __import__(mn, {}, {}, "*") for on in dir(m): o = getattr(m, on) if (inspect.isclass(o) and issubclass(o, AlarmPlugin) and o.__module__.startswith(mn)): assert o.name self.plugins[o.name] = o(self) def cleaned_query(self, q): q = q.copy() status = q["status"] if "status" in q else "A" for p in self.ignored_params: if p in q: del q[p] for p in (self.limit_param, self.page_param, self.start_param, self.format_param, self.sort_param, self.query_param, self.only_param): if p in q: del q[p] # Normalize parameters for p in q: qp = p.split("__")[0] if qp in self.clean_fields: q[p] = self.clean_fields[qp].clean(q[p]) # Exclude maintenance if "maintenance" not in q: q["maintenance"] = "hide" if q["maintenance"] == "hide": q["managed_object__nin"] = Maintenance.currently_affected() elif q["maintenance"] == "only": q["managed_object__in"] = Maintenance.currently_affected() del q["maintenance"] if "administrative_domain" in q: q["adm_path"] = int(q["administrative_domain"]) q.pop("administrative_domain") if "segment" in q: q["segment_path"] = bson.ObjectId(q["segment"]) q.pop("segment") if "managedobjectselector" in q: s = SelectorCache.objects.filter( selector=q["managedobjectselector"]).values_list("object") if "managed_object__in" in q: q["managed_object__in"] = list( set(q["managed_object__in"]).intersection(s)) else: q["managed_object__in"] = s q.pop("managedobjectselector") if "cleared_after" in q: q["clear_timestamp__gte"] = datetime.datetime.now( ) - datetime.timedelta(seconds=int(q["cleared_after"])) q.pop("cleared_after") # if "wait_tt" in q: q["wait_tt__exists"] = True q["wait_ts__exists"] = False del q["wait_tt"] # if "collapse" in q: c = q["collapse"] del q["collapse"] if c != "0": q["root__exists"] = False if status == "C": if ("timestamp__gte" not in q and "timestamp__lte" not in q and "escalation_tt__contains" not in q and "managed_object" not in q): q["timestamp__gte"] = datetime.datetime.now( ) - self.DEFAULT_ARCH_ALARM return q def instance_to_dict(self, o, fields=None): s = AlarmSeverity.get_severity(o.severity) n_events = (ActiveEvent.objects.filter(alarms=o.id).count() + ArchivedEvent.objects.filter(alarms=o.id).count()) mtc = o.managed_object.id in Maintenance.currently_affected() if o.status == "C": # For archived alarms mtc = Maintenance.objects.filter( start__lte=o.clear_timestamp, stop__lte=o.timestamp, affected_objects__in=[ MaintenanceObject(object=o.managed_object) ]).count() > 0 d = { "id": str(o.id), "status": o.status, "managed_object": o.managed_object.id, "managed_object__label": o.managed_object.name, "administrative_domain": o.managed_object.administrative_domain_id, "administrative_domain__label": o.managed_object.administrative_domain.name, "severity": o.severity, "severity__label": s.name, "alarm_class": str(o.alarm_class.id), "alarm_class__label": o.alarm_class.name, "timestamp": self.to_json(o.timestamp), "subject": o.subject, "events": n_events, "duration": o.duration, "clear_timestamp": self.to_json(o.clear_timestamp) if o.status == "C" else None, "row_class": s.style.css_class_name, "segment__label": o.managed_object.segment.name, "segment": str(o.managed_object.segment.id), "location_1": self.location(o.managed_object.container.id)[0] if o.managed_object.container else "", "location_2": self.location(o.managed_object.container.id)[1] if o.managed_object.container else "", "escalation_tt": o.escalation_tt, "escalation_error": o.escalation_error, "platform": o.managed_object.platform.name if o.managed_object.platform else "", "address": o.managed_object.address, "isInMaintenance": mtc, "summary": self.f_glyph_summary({ "subscriber": SummaryItem.items_to_dict(o.total_subscribers), "service": SummaryItem.items_to_dict(o.total_services) }), "total_objects": sum(x.summary for x in o.total_objects) } if fields: d = dict((k, d[k]) for k in fields) return d def queryset(self, request, query=None): """ Filter records for lookup """ status = request.GET.get("status", "A") if status not in self.model_map: raise Exception("Invalid status") model = self.model_map[status] if request.user.is_superuser: return model.objects.filter( read_preference=ReadPreference.SECONDARY_PREFERRED).all() else: return model.objects.filter( adm_path__in=UserAccess.get_domains(request.user), read_preference=ReadPreference.SECONDARY_PREFERRED) @view(url=r"^$", access="launch", method=["GET"], api=True) def api_list(self, request): return self.list_data(request, self.instance_to_dict) @view(url=r"^(?P<id>[a-z0-9]{24})/$", method=["GET"], api=True, access="launch") def api_alarm(self, request, id): alarm = get_alarm(id) if not alarm: self.response_not_found() user = request.user d = self.instance_to_dict(alarm) d["body"] = alarm.body d["symptoms"] = alarm.alarm_class.symptoms d["probable_causes"] = alarm.alarm_class.probable_causes d["recommended_actions"] = alarm.alarm_class.recommended_actions d["vars"] = sorted(alarm.vars.items()) d["status"] = alarm.status d["status__label"] = {"A": "Active", "C": "Cleared"}[alarm.status] # Managed object properties mo = alarm.managed_object d["managed_object_address"] = mo.address d["managed_object_profile"] = mo.profile.name d["managed_object_platform"] = mo.platform.name if mo.platform else "" d["managed_object_version"] = mo.version.version if mo.version else "" d["segment"] = mo.segment.name d["segment_id"] = str(mo.segment.id) d["segment_path"] = " | ".join( NetworkSegment.get_by_id(p).name for p in NetworkSegment.get_path(mo.segment)) if mo.container: cp = [] c = mo.container.id while c: try: o = Object.objects.get(id=c) if o.container: cp.insert(0, o.name) c = o.container.id if o.container else None except DoesNotExist: break d["container_path"] = " | ".join(cp) if not self.location(mo.container.id)[0]: d["address_path"] = None else: d["address_path"] = ", ".join(self.location(mo.container.id)) d["tags"] = mo.tags # Log if alarm.log: d["log"] = [{ "timestamp": self.to_json(l.timestamp), "from_status": l.from_status, "to_status": l.to_status, "message": l.message } for l in alarm.log] # Events events = [] for ec in ActiveEvent, ArchivedEvent: for e in ec.objects.filter(alarms=alarm.id): events += [{ "id": str(e.id), "event_class": str(e.event_class.id), "event_class__label": e.event_class.name, "timestamp": self.to_json(e.timestamp), "status": e.status, "managed_object": e.managed_object.id, "managed_object__label": e.managed_object.name, "subject": e.subject }] if events: d["events"] = events # Alarms children = self.get_nested_alarms(alarm) if children: d["alarms"] = {"expanded": True, "children": children} # Subscribers if alarm.status == "A": d["subscribers"] = self.get_alarm_subscribers(alarm) d["is_subscribed"] = user in alarm.subscribers # Apply plugins plugins = [] acp = alarm.alarm_class.plugins or [] acp += [self.diagnostic_plugin] for p in acp: if p.name in self.plugins: plugin = self.plugins[p.name] dd = plugin.get_data(alarm, p.config) if "plugins" in dd: plugins += dd["plugins"] del dd["plugins"] d.update(dd) if plugins: d["plugins"] = plugins return d def get_alarm_subscribers(self, alarm): """ JSON-serializable subscribers :param alarm: :return: """ subscribers = [] for u in alarm.subscribers: try: u = User.objects.get(id=u) subscribers += [{ "id": u.id, "name": " ".join([u.first_name, u.last_name]), "login": u.username }] except User.DoesNotExist: pass return subscribers def get_nested_alarms(self, alarm): """ Return nested alarms as a part of NodeInterface :param alarm: :return: """ children = [] for ac in (ActiveAlarm, ArchivedAlarm): for a in ac.objects.filter(root=alarm.id): s = AlarmSeverity.get_severity(a.severity) c = { "id": str(a.id), "subject": a.subject, "alarm_class": str(a.alarm_class.id), "alarm_class__label": a.alarm_class.name, "managed_object": a.managed_object.id, "managed_object__label": a.managed_object.name, "timestamp": self.to_json(a.timestamp), "iconCls": "icon_error", "row_class": s.style.css_class_name } nc = self.get_nested_alarms(a) if nc: c["children"] = nc c["expanded"] = True else: c["leaf"] = True children += [c] return children @view(url=r"^(?P<id>[a-z0-9]{24})/post/", method=["POST"], api=True, access="launch", validate={"msg": UnicodeParameter()}) def api_post(self, request, id, msg): alarm = get_alarm(id) if not alarm: self.response_not_found() alarm.log_message("%s: %s" % (request.user.username, msg)) return True @view(url=r"^(?P<id>[a-z0-9]{24})/subscribe/", method=["POST"], api=True, access="launch") def api_subscribe(self, request, id): alarm = get_alarm(id) if not alarm: return self.response_not_found() if alarm.status == "A": alarm.subscribe(request.user) return self.get_alarm_subscribers(alarm) else: return [] @view(url=r"^(?P<id>[a-z0-9]{24})/unsubscribe/", method=["POST"], api=True, access="launch") def api_unsubscribe(self, request, id): alarm = get_alarm(id) if not alarm: return self.response_not_found() if alarm.status == "A": alarm.unsubscribe(request.user) return self.get_alarm_subscribers(alarm) else: return [] @view(url=r"^(?P<id>[a-z0-9]{24})/clear/", method=["POST"], api=True, access="launch") def api_clear(self, request, id): alarm = get_alarm(id) if alarm.status == "A": alarm.clear_alarm("Cleared by %s" % request.user) return True @view(url=r"^(?P<id>[a-z0-9]{24})/set_root/", method=["POST"], api=True, access="launch", validate={"root": StringParameter()}) def api_set_root(self, request, id, root): alarm = get_alarm(id) r = get_alarm(root) if not r: return self.response_not_found() alarm.set_root(r) return True @view(url="notification/$", method=["GET"], api=True, access="launch") def api_notification(self, request): delta = request.GET.get("delta") n = 0 sound = None volume = 0 if delta: dt = datetime.timedelta(seconds=int(delta)) t0 = datetime.datetime.now() - dt r = list(ActiveAlarm._get_collection().aggregate([{ "$match": { "timestamp": { "$gt": t0 } } }, { "$group": { "_id": "$item", "severity": { "$max": "$severity" } } }])) if r: s = AlarmSeverity.get_severity(r[0]["severity"]) if s and s.sound and s.volume: sound = "/ui/pkg/nocsound/%s.mp3" % s.sound volume = float(s.volume) / 100.0 return {"new_alarms": n, "sound": sound, "volume": volume} @classmethod def f_glyph_summary(cls, s, collapse=False): def be_true(p): return True def be_show(p): return p.show_in_summary def get_summary(d, profile): v = [] if hasattr(profile, "show_in_summary"): show_in_summary = be_show else: show_in_summary = be_true for p, c in sorted(d.items(), key=lambda x: -x[1]): pv = profile.get_by_id(p) if pv and show_in_summary(pv): if collapse and c < 2: badge = "" else: badge = " <span class=\"x-display-tag\">%s</span>" % c v += [ "<i class=\"%s\" title=\"%s\"></i>%s" % (pv.glyph, pv.name, badge) ] return "<span class='x-summary'>%s</span>" % " ".join(v) if not isinstance(s, dict): return "" r = [] if "subscriber" in s: from noc.crm.models.subscriberprofile import SubscriberProfile r += [get_summary(s["subscriber"], SubscriberProfile)] if "service" in s: from noc.sa.models.serviceprofile import ServiceProfile r += [get_summary(s["service"], ServiceProfile)] r = [x for x in r if x] return " ".join(r) @view(url=r"^(?P<id>[a-z0-9]{24})/escalate/", method=["GET"], api=True, access="escalate") def api_escalation_alarm(self, request, id): alarm = get_alarm(id) if alarm.status == "A": AlarmEscalation.watch_escalations(alarm) return {'status': True} else: return { 'status': False, 'error': 'The alarm is not active at the moment' } def location(self, id): """ Return geo address for Managed Objects """ def chunkIt(seq, num): avg = len(seq) / float(num) out = [] last = 0.0 while last < len(seq): out.append(seq[int(last):int(last + avg)]) last += avg return out location = [] address = Object.get_by_id(id).get_address_text() if address: for res in address.split(","): adr = normalize_division(res.strip().decode("utf-8").lower()) if None in adr and "" in adr: continue if None in adr: location += [adr[1].title().strip()] else: location += [' '.join(adr).title().strip()] res = chunkIt(location, 2) location_1 = ", ".join(res[0]) location_2 = ", ".join(res[1]) return [location_1, location_2] else: return ["", ""]
class ReportLinkDetailApplication(ExtApplication): menu = _("Reports") + "|" + _("Link Detail") title = _("Link Detail") SEGMENT_PATH_DEPTH = 7 CONTAINER_PATH_DEPTH = 7 @view( "^download/$", method=["GET"], access="launch", api=True, validate={ "administrative_domain": StringParameter(required=False), "pool": StringParameter(required=False), "segment": StringParameter(required=False), "selector": StringParameter(required=False), "ids": StringParameter(required=False), "is_managed": BooleanParameter(required=False), "avail_status": BooleanParameter(required=False), "columns": StringParameter(required=False), "o_format": StringParameter(choices=["csv", "csv_zip", "xlsx"]), }, ) def api_report( self, request, o_format, is_managed=None, administrative_domain=None, selector=None, pool=None, segment=None, avail_status=False, columns=None, ids=None, enable_autowidth=False, ): def row(row): def qe(v): if v is None: return "" if isinstance(v, str): return smart_text(v) elif isinstance(v, datetime.datetime): return v.strftime("%Y-%m-%d %H:%M:%S") elif not isinstance(v, str): return str(v) else: return v return [qe(x) for x in row] def translate_row(row, cmap): return [row[i] for i in cmap] type_columns = ["Up/10G", "Up/1G", "Up/100M", "Down/-", "-"] cols = [ "object1_admin_domain", # "id", "object1_name", "object1_address", "object1_platform", "object1_segment", "object1_tags", "object1_iface", "object1_descr", "object1_speed", "object2_admin_domain", "object2_name", "object2_address", "object2_platform", "object2_segment", "object2_tags", "object2_iface", "object2_descr", "object2_speed", "link_proto", "last_seen", ] header_row = [ "OBJECT1_ADMIN_DOMAIN", "OBJECT1_NAME", "OBJECT1_ADDRESS", "OBJECT1_PLATFORM", "OBJECT1_SEGMENT", "OBJECT1_TAGS", "OBJECT1_IFACE", "OBJECT1_DESCR", "OBJECT1_SPEED", "OBJECT2_ADMIN_DOMAIN", "OBJECT2_NAME", "OBJECT2_ADDRESS", "OBJECT2_PLATFORM", "OBJECT2_SEGMENT", "OBJECT2_TAGS", "OBJECT2_IFACE", "OBJECT2_DESCR", "OBJECT2_SPEED", "LINK_PROTO", "LAST_SEEN", ] if columns: cmap = [] for c in columns.split(","): try: cmap += [cols.index(c)] except ValueError: continue else: cmap = list(range(len(cols))) r = [translate_row(header_row, cmap)] if "interface_type_count" in columns.split(","): r[-1].extend(type_columns) # self.logger.info(r) # self.logger.info("---------------------------------") # print("-----------%s------------%s" % (administrative_domain, columns)) p = Pool.get_by_name(pool or "default") mos = ManagedObject.objects.filter() if request.user.is_superuser and not administrative_domain and not selector and not segment: mos = ManagedObject.objects.filter(pool=p) if ids: mos = ManagedObject.objects.filter(id__in=[ids]) if is_managed is not None: mos = ManagedObject.objects.filter(is_managed=is_managed) if pool: mos = mos.filter(pool=p) if not request.user.is_superuser: mos = mos.filter( administrative_domain__in=UserAccess.get_domains(request.user)) if administrative_domain: ads = AdministrativeDomain.get_nested_ids( int(administrative_domain)) mos = mos.filter(administrative_domain__in=ads) if selector: selector = ManagedObjectSelector.get_by_id(int(selector)) mos = mos.filter(selector.Q) if segment: segment = NetworkSegment.objects.filter(id=segment).first() if segment: mos = mos.filter(segment__in=segment.get_nested_ids()) mos_id = list(mos.values_list("id", flat=True)) rld = ReportLinksDetail(mos_id) mo_resolv = { mo[0]: mo[1:] for mo in ManagedObject.objects.filter().values_list( "id", "administrative_domain__name", "name", "address", "segment", "platform", "labels", ) } for link in rld.out: if len(rld.out[link]) != 2: # Multilink or bad link continue s1, s2 = rld.out[link] seg1, seg2 = None, None if "object1_segment" in columns.split( ",") or "object2_segment" in columns.split(","): seg1, seg2 = mo_resolv[s1["mo"][0]][3], mo_resolv[s2["mo"] [0]][3] plat1, plat2 = None, None if "object1_platform" in columns.split( ",") or "object2_platform" in columns.split(","): plat1, plat2 = mo_resolv[s1["mo"][0]][4], mo_resolv[s2["mo"] [0]][4] r += [ translate_row( row([ mo_resolv[s1["mo"][0]][0], mo_resolv[s1["mo"][0]][1], mo_resolv[s1["mo"][0]][2], "" if not plat1 else Platform.get_by_id(plat1), "" if not seg1 else NetworkSegment.get_by_id(seg1), ";".join(mo_resolv[s1["mo"][0]][5] or []), s1["iface_n"][0], s1.get("iface_descr")[0] if s1.get("iface_descr") else "", s1.get("iface_speed")[0] if s1.get("iface_speed") else 0, mo_resolv[s2["mo"][0]][0], mo_resolv[s2["mo"][0]][1], mo_resolv[s2["mo"][0]][2], "" if not plat2 else Platform.get_by_id(plat2), "" if not seg2 else NetworkSegment.get_by_id(seg2), ";".join(mo_resolv[s2["mo"][0]][5] or []), s2["iface_n"][0], s2.get("iface_descr")[0] if s2.get("iface_descr") else "", s2.get("iface_speed")[0] if s2.get("iface_speed") else 0, s2.get("dis_method", ""), s2.get("last_seen", ""), ]), cmap, ) ] filename = "links_detail_report_%s" % datetime.datetime.now().strftime( "%Y%m%d") if o_format == "csv": response = HttpResponse(content_type="text/csv") response[ "Content-Disposition"] = 'attachment; filename="%s.csv"' % filename writer = csv.writer(response, dialect="excel", delimiter=",", quoting=csv.QUOTE_MINIMAL) writer.writerows(r) return response elif o_format == "csv_zip": response = BytesIO() f = TextIOWrapper(TemporaryFile(mode="w+b"), encoding="utf-8") writer = csv.writer(f, dialect="excel", delimiter=";", quotechar='"') writer.writerows(r) f.seek(0) with ZipFile(response, "w", compression=ZIP_DEFLATED) as zf: zf.writestr("%s.csv" % filename, f.read()) zf.filename = "%s.csv.zip" % filename # response = HttpResponse(content_type="text/csv") response.seek(0) response = HttpResponse(response.getvalue(), content_type="application/zip") response[ "Content-Disposition"] = 'attachment; filename="%s.csv.zip"' % filename return response elif o_format == "xlsx": response = BytesIO() wb = xlsxwriter.Workbook(response) cf1 = wb.add_format({"bottom": 1, "left": 1, "right": 1, "top": 1}) ws = wb.add_worksheet("Objects") max_column_data_length = {} for rn, x in enumerate(r): for cn, c in enumerate(x): if rn and (r[0][cn] not in max_column_data_length or len(str(c)) > max_column_data_length[r[0][cn]]): max_column_data_length[r[0][cn]] = len(str(c)) ws.write(rn, cn, c, cf1) ws.autofilter(0, 0, rn, cn) ws.freeze_panes(1, 0) for cn, c in enumerate(r[0]): # Set column width width = get_column_width(c) if enable_autowidth and width < max_column_data_length[c]: width = max_column_data_length[c] ws.set_column(cn, cn, width=width) wb.close() response.seek(0) response = HttpResponse(response.getvalue(), content_type="application/vnd.ms-excel") # response = HttpResponse( # content_type="application/x-ms-excel") response[ "Content-Disposition"] = 'attachment; filename="%s.xlsx"' % filename response.close() return response
class ReportObjectDetailApplication(ExtApplication): menu = _("Reports") + "|" + _("Object Detail") title = _("Object Detail") SEGMENT_PATH_DEPTH = 7 CONTAINER_PATH_DEPTH = 7 def get_report_object(self, user=None, is_managed=None, adm=None, selector=None, pool=None, segment=None, ids=None): mos = ManagedObject.objects.filter() if user.is_superuser and not adm and not selector and not segment: mos = ManagedObject.objects.filter() if ids: mos = ManagedObject.objects.filter(id__in=[ids]) if is_managed is not None: mos = ManagedObject.objects.filter(is_managed=is_managed) if pool: p = Pool.get_by_name(pool or "default") mos = mos.filter(pool=p) if not user.is_superuser: mos = mos.filter( administrative_domain__in=UserAccess.get_domains(user)) if adm: ads = AdministrativeDomain.get_nested_ids(int(adm)) mos = mos.filter(administrative_domain__in=ads) if selector: selector = ManagedObjectSelector.get_by_id(int(selector)) mos = mos.filter(selector.Q) if segment: segment = NetworkSegment.objects.filter(id=segment).first() if segment: mos = mos.filter(segment__in=segment.get_nested_ids()) return mos @view( "^download/$", method=["GET"], access="launch", api=True, validate={ "administrative_domain": StringParameter(required=False), "pool": StringParameter(required=False), "segment": StringParameter(required=False), "selector": StringParameter(required=False), "ids": StringParameter(required=False), "detail_stat": StringParameter(required=False), "is_managed": BooleanParameter(required=False), "avail_status": BooleanParameter(required=False), "columns": StringParameter(required=False), "o_format": StringParameter(choices=["csv", "xlsx"]), }, ) def api_report( self, request, o_format, is_managed=None, administrative_domain=None, selector=None, pool=None, segment=None, avail_status=False, columns=None, ids=None, detail_stat=None, enable_autowidth=False, ): def row(row): def qe(v): if v is None: return "" if isinstance(v, unicode): return v.encode("utf-8") elif isinstance(v, datetime.datetime): return v.strftime("%Y-%m-%d %H:%M:%S") elif not isinstance(v, str): return str(v) else: return v return [qe(x) for x in row] def translate_row(row, cmap): return [row[i] for i in cmap] type_columns = ["Up/10G", "Up/1G", "Up/100M", "Up/10M", "Down/-", "-"] cols = [ "id", "object_name", "object_address", "object_hostname", "object_status", "profile_name", "object_profile", "object_vendor", "object_platform", "object_attr_hwversion", "object_version", "object_attr_bootprom", "object_serial", "object_attr_patch", "auth_profile", "avail", "admin_domain", "container", "segment", "phys_interface_count", "link_count", "last_config_ts" # "discovery_problem" # "object_tags" # "sorted_tags" # "object_caps" # "interface_type_count" ] header_row = [ "ID", "OBJECT_NAME", "OBJECT_ADDRESS", "OBJECT_HOSTNAME", "OBJECT_STATUS", "PROFILE_NAME", "OBJECT_PROFILE", "OBJECT_VENDOR", "OBJECT_PLATFORM", "OBJECT_HWVERSION", "OBJECT_VERSION", "OBJECT_BOOTPROM", "OBJECT_SERIAL", "OBJECT_ATTR_PATCH", "AUTH_PROFILE", "AVAIL", "ADMIN_DOMAIN", "CONTAINER", "SEGMENT", "PHYS_INTERFACE_COUNT", "LINK_COUNT", "LAST_CONFIG_TS", ] # "DISCOVERY_PROBLEM" # "ADM_PATH # "DISCOVERY_PROBLEM" # "OBJECT_TAGS" # "SORTED_TAGS" # "OBJECT_CAPS" # "INTERFACE_TYPE_COUNT" if columns: cmap = [] for c in columns.split(","): try: cmap += [cols.index(c)] except ValueError: continue else: cmap = list(range(len(cols))) r = [translate_row(header_row, cmap)] mos = self.get_report_object(request.user, is_managed, administrative_domain, selector, pool, segment, ids) columns_filter = set(columns.split(",")) mos_id = tuple(mos.order_by("id").values_list("id", flat=True)) mos_filter = None if detail_stat: ref = ReportModelFilter() ids = list(six.itervalues(ref.proccessed(detail_stat))) mos_filter = set(mos_id).intersection(ids[0]) mos_id = sorted(mos_filter) avail = {} if "avail" in columns_filter: avail = ObjectStatus.get_statuses(mos_id) link_count = iter(ReportObjectLinkCount(mos_id)) iface_count = iter(ReportObjectIfacesTypeStat(mos_id)) if "container" in columns_filter: container_lookup = iter(ReportContainerData(mos_id)) else: container_lookup = None if "object_serial" in columns_filter: container_serials = iter(ReportContainer(mos_id)) else: container_serials = None if "interface_type_count" in columns_filter: iss = iter(ReportObjectIfacesStatusStat(mos_id)) else: iss = None if "object_attr_patch" in columns_filter or "object_serial" in columns_filter: roa = iter(ReportObjectAttributes(mos_id)) else: roa = None hn = iter(ReportObjectsHostname1(mos_id)) rc = iter(ReportObjectConfig(mos_id)) # ccc = iter(ReportObjectCaps(mos_id)) if "adm_path" in columns_filter: ad_path = ReportAdPath() r[-1].extend([_("ADM_PATH1"), _("ADM_PATH1"), _("ADM_PATH1")]) if "interface_type_count" in columns_filter: r[-1].extend(type_columns) if "object_caps" in columns_filter: object_caps = ReportObjectCaps(mos_id) caps_columns = list(six.itervalues(object_caps.ATTRS)) ccc = iter(object_caps) r[-1].extend(caps_columns) if "object_tags" in columns_filter: r[-1].extend([_("OBJECT_TAGS")]) if "sorted_tags" in columns_filter: tags = set() for s in (ManagedObject.objects.filter().exclude( tags=None).values_list("tags", flat=True).distinct()): tags.update(set(s)) tags_o = sorted([t for t in tags if "{" not in t]) r[-1].extend(tags_o) if "discovery_problem" in columns.split(","): discovery_result = ReportDiscoveryResult(mos_id) discovery_result.safe_output = True discovery_result.unknown_value = ([""] * len(discovery_result.ATTRS), ) dp_columns = discovery_result.ATTRS dp = iter(discovery_result) r[-1].extend(dp_columns) for ( mo_id, name, address, is_managed, sa_profile, o_profile, auth_profile, ad, m_segment, vendor, platform, version, tags, ) in (mos.values_list( "id", "name", "address", "is_managed", "profile", "object_profile__name", "auth_profile__name", "administrative_domain__name", "segment", "vendor", "platform", "version", "tags", ).order_by("id").iterator()): if (mos_filter and mo_id not in mos_filter) or not mos_id: continue if container_serials: mo_serials = next(container_serials) else: mo_serials = [{}] if container_lookup: mo_continer = next(container_lookup) else: mo_continer = ("", ) if roa: serial, hw_ver, boot_prom, patch = next(roa)[0] # noqa else: serial, hw_ver, boot_prom, patch = "", "", "", "" # noqa r.append( translate_row( row([ mo_id, name, address, next(hn)[0], "managed" if is_managed else "unmanaged", Profile.get_by_id(sa_profile), o_profile, Vendor.get_by_id(vendor) if vendor else "", Platform.get_by_id(platform) if platform else "", hw_ver, Firmware.get_by_id(version) if version else "", boot_prom, # Serial mo_serials[0].get("serial", "") or serial, patch or "", auth_profile, _("Yes") if avail.get(mo_id, None) else _("No"), ad, mo_continer[0], NetworkSegment.get_by_id(m_segment) if m_segment else "", next(iface_count)[0], next(link_count)[0], next(rc)[0], ]), cmap, )) if "adm_path" in columns_filter: r[-1].extend([ad] + list(ad_path[ad])) if "interface_type_count" in columns_filter: r[-1].extend(next(iss)[0]) if "object_caps" in columns_filter: r[-1].extend(next(ccc)[0]) if "object_tags" in columns_filter: r[-1].append(",".join(tags if tags else [])) if "sorted_tags" in columns_filter: out_tags = [""] * len(tags_o) try: if tags: for m in tags: out_tags[tags_o.index(m)] = m except ValueError: logger.warning("Bad value for tag: %s", m) r[-1].extend(out_tags) if "discovery_problem" in columns_filter: r[-1].extend(next(dp)[0]) filename = "mo_detail_report_%s" % datetime.datetime.now().strftime( "%Y%m%d") if o_format == "csv": response = HttpResponse(content_type="text/csv") response[ "Content-Disposition"] = 'attachment; filename="%s.csv"' % filename writer = csv.writer(response, dialect="excel", delimiter=";", quotechar='"') writer.writerows(r) return response elif o_format == "xlsx": response = StringIO() wb = xlsxwriter.Workbook(response) cf1 = wb.add_format({"bottom": 1, "left": 1, "right": 1, "top": 1}) ws = wb.add_worksheet("Objects") max_column_data_length = {} for rn, x in enumerate(r): for cn, c in enumerate(x): if rn and (r[0][cn] not in max_column_data_length or len(str(c)) > max_column_data_length[r[0][cn]]): max_column_data_length[r[0][cn]] = len(str(c)) ws.write(rn, cn, c, cf1) # for ws.autofilter(0, 0, rn, cn) ws.freeze_panes(1, 0) for cn, c in enumerate(r[0]): # Set column width width = get_column_width(c) if enable_autowidth and width < max_column_data_length[c]: width = max_column_data_length[c] ws.set_column(cn, cn, width=width) wb.close() response.seek(0) response = HttpResponse(response.getvalue(), content_type="application/vnd.ms-excel") # response = HttpResponse( # content_type="application/x-ms-excel") response[ "Content-Disposition"] = 'attachment; filename="%s.xlsx"' % filename response.close() return response
class UserApplication(ExtModelApplication): model = User glyph = "user" menu = [_("Setup"), _("Users")] icon = "icon_user" title = _("Users") app_alias = "auth" query_condition = "icontains" query_fields = ["username"] default_ordering = ["username"] clean_fields = { "username": UsernameParameter(), "first_name": StringParameter(default=""), "last_name": StringParameter(default=""), "email": StringParameter(default=""), } ignored_fields = {"id", "bi_id", "password"} custom_m2m_fields = {"permissions": Permission} @classmethod def apps_permissions_list(cls): r = [] apps = list(site.apps) perms = Permission.objects.values_list("name", flat=True) for module in [m for m in settings.INSTALLED_APPS if m.startswith("noc.")]: mod = module[4:] m = __import__("noc.services.web.apps.%s" % mod, {}, {}, "MODULE_NAME") for app in [app for app in apps if app.startswith(mod + ".")]: app_perms = sorted([p for p in perms if p.startswith(app.replace(".", ":") + ":")]) a = site.apps[app] if app_perms: for p in app_perms: r += [ { "module": m.MODULE_NAME, "title": str(a.title), "name": p, "status": False, } ] return r @view(method=["GET"], url=r"^(?P<id>\d+)/?$", access="read", api=True) def api_read(self, request, id): """ Returns dict with object's fields and values """ try: o = self.queryset(request).get(**{self.pk: int(id)}) except self.model.DoesNotExist: return HttpResponse("", status=self.NOT_FOUND) only = request.GET.get(self.only_param) if only: only = only.split(",") return self.response(self.instance_to_dict_get(o, fields=only), status=self.OK) def instance_to_dict_get(self, o, fields=None): r = super().instance_to_dict(o, fields) del r["password"] r["permissions"] = self.apps_permissions_list() current_perms = Permission.get_user_permissions(o) if current_perms: for p in r["permissions"]: if p["name"] in current_perms: p["status"] = True return r def clean_list_data(self, data): """ Finally process list_data result. Override to enrich with additional fields :param data: :return: """ r = super().apply_bulk_fields(data=data) for x in r: if "password" in x: del x["password"] return r def update_m2m(self, o, name, values): if values is None: return # Do not touch if name == "permissions": Permission.set_user_permissions(user=o, perms=values) else: super().update_m2m(o, name, values) @view(method=["GET"], url=r"^new_permissions/$", access="read", api=True) def api_read_permission(self, request): """ Returns dict available permissions """ return self.response( {"data": {"user_permissions": self.apps_permissions_list()}}, status=self.OK ) @view( url=r"^(\d+)/password/$", method=["POST"], access="change", validate={"password": StringParameter(required=True)}, ) def view_change_password(self, request, object_id, password): """ Change user's password :param request: :param object_id: :param password: :return: """ if not request.user.is_superuser: return self.response_forbidden("Permission denied") user = self.get_object_or_404(self.model, pk=object_id) user.set_password(password) user.save() return self.response({"result": "Password changed", "status": True}, self.OK) def can_delete(self, user, obj=None): """Disable 'Delete' button""" return False
class UserProfileApplication(ExtApplication): """ main.userprofile application """ title = _("User Profile") implied_permissions = {"launch": ["main:timepattern:lookup"]} @view(url="^$", method=["GET"], access=PermitLogged(), api=True) def api_get(self, request): user = request.user language = user.preferred_language contacts = [{ "time_pattern": c.time_pattern.id, "time_pattern__label": c.time_pattern.name, "notification_method": c.notification_method, "params": c.params, } for c in UserContact.objects.filter(user=user)] return { "username": user.username, "name": (" ".join([x for x in (user.first_name, user.last_name) if x])).strip(), "email": user.email, "preferred_language": language or "en", "contacts": contacts, "groups": [g.name for g in user.groups.all().order_by("name")], } @view( url="^$", method=["POST"], access=PermitLogged(), api=True, validate={ "preferred_language": StringParameter(choices=[x[0] for x in LANGUAGES]), "contacts": ListOfParameter(element=DictParameter( attrs={ "time_pattern": ModelParameter(TimePattern), "notification_method": StringParameter(choices=[ x[0] for x in USER_NOTIFICATION_METHOD_CHOICES ]), "params": StringParameter(), })), }, ) def api_save(self, request, preferred_language, contacts): user = request.user user.preferred_language = preferred_language user.save() # Setup contacts UserContact.objects.filter(user=user).delete() for c in contacts: UserContact( user=user, time_pattern=c["time_pattern"], notification_method=c["notification_method"], params=c["params"], ).save() return True
class InvApplication(ExtApplication): """ inv.inv application """ title = "Inventory" menu = "Inventory" # Undeletable nodes UNDELETABLE = set([ # Global Lost&Found "b0fae773-b214-4edf-be35-3468b53b03f2" ]) def __init__(self, *args, **kwargs): ExtApplication.__init__(self, *args, **kwargs) # Load plugins from plugins.base import InvPlugin self.plugins = {} for f in os.listdir("inv/apps/inv/plugins/"): if (not f.endswith(".py") or f == "base.py" or f.startswith("_")): continue mn = "noc.inv.apps.inv.plugins.%s" % f[:-3] m = __import__(mn, {}, {}, "*") for on in dir(m): o = getattr(m, on) if (inspect.isclass(o) and issubclass(o, InvPlugin) and o.__module__.startswith(mn)): assert o.name self.plugins[o.name] = o(self) def get_root(self): """ Get root container """ if not hasattr(self, "root_container"): rm = ObjectModel.objects.get(name="Root") rc = list(Object.objects.filter(model=rm)) if len(rc) == 0: raise Exception("No root object") elif len(rc) == 1: self.root_container = rc[0] return self.root_container else: raise Exception("Multiple root objects") else: return self.root_container def get_plugin_data(self, name): return { "name": name, "xtype": self.plugins[name].js } @view("^node/$", method=["GET"], access="read", api=True) def api_node(self, request): container = None if request.GET and "node" in request.GET: container = request.GET["node"] if container == "root": container = self.get_root() elif not is_objectid(container): raise Exception("Invalid node") else: container = self.get_object_or_404(Object, id=container) r = [] if not container: container = self.get_root() # Collect children objects 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() ] # 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 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")] # 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 @view("^add_group/$", method=["POST"], access="create_group", api=True, validate={ "container": ObjectIdParameter(required=False), "type": ObjectIdParameter(), "name": UnicodeParameter(), "serial": UnicodeParameter(required=False) }) def api_add_group(self, request, type, name, container=None, serial=None): if container is None: c = self.get_root() else: c = self.get_object_or_404(Object, id=container) m = self.get_object_or_404(ObjectModel, id=type) o = Object(name=name, model=m, container=c.id) 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) @view("^remove_group/$", method=["DELETE"], access="remove_group", api=True, validate={ "container": ObjectIdParameter(required=True) }) def api_remove_group(self, request, container=None): c = self.get_object_or_404(Object, id=container) c.delete() return True @view("^insert/$", method=["POST"], access="reorder", api=True, validate={ "container": ObjectIdParameter(required=False), "objects": ListOfParameter(element=ObjectIdParameter()), "position": StringParameter() }) def api_insert(self, request, container, objects, position): c = self.get_object_or_404(Object, id=container) o = [] for r in objects: o += [self.get_object_or_404(Object, id=r)] if position == "append": for x in o: x.put_into(c) elif position in ("before", "after"): cc = self.get_object_or_404(Object, id=c.container) for x in o: x.put_into(cc) return True @view("^(?P<id>[0-9a-f]{24})/path/$", method=["GET"], access="read", api=True) def api_get_path(self, request, id): o = self.get_object_or_404(Object, id=id) path = [{ "id": str(o.id), "name": o.name }] root = self.get_root().id while o.container and o.container != root: o = Object.objects.get(id=o.container) path = [{ "id": str(o.id), "name": o.name }] + path return path
class VCApplication(ExtModelApplication): """ VC application """ title = _("VC") menu = _("Virtual Circuits") model = VC query_fields = ["name", "description"] query_condition = "icontains" int_query_fields = ["l1", "l2"] implied_permissions = {"read": ["vc:vcdomain:lookup", "main:style:lookup"]} def get_vc_domain_objects(self, vc_domain): return vc_domain.managedobject_set.all() def lookup_vcfilter(self, q, name, value): """ Resolve __vcflter lookups :param q: :param name: :param value: :return: """ value = ModelParameter(VCFilter).clean(value) x = value.to_sql(name) try: q[None] += [x] except KeyError: q[None] = [x] @cachedmethod(key="vc-interface-count-%s") def get_vc_interfaces_count(self, vc_id): vc = VC.get_by_id(vc_id) if not vc: return 0 objects = vc.vc_domain.managedobject_set.values_list("id", flat=True) l1 = vc.l1 n = SubInterface.objects.filter( Q(managed_object__in=objects) & ( Q(untagged_vlan=l1, enabled_afi=["BRIDGE"]) | Q(tagged_vlans=l1, enabled_afi=["BRIDGE"]) | Q(vlan_ids=l1) ) ).count() return n @cachedmethod(key="vc-prefixes-%s") def get_vc_prefixes(self, vc_id): vc = VC.get_by_id(vc_id) if not vc: return [] objects = vc.vc_domain.managedobject_set.values_list("id", flat=True) ipv4 = set() ipv6 = set() # @todo: Exact match on vlan_ids for si in SubInterface.objects.filter( Q(managed_object__in=objects) & Q(vlan_ids=vc.l1) & (Q(enabled_afi=["IPv4"]) | Q(enabled_afi=["IPv6"])) ).only("enabled_afi", "ipv4_addresses", "ipv6_addresses"): if "IPv4" in si.enabled_afi: ipv4.update([IP.prefix(ip).first for ip in si.ipv4_addresses]) if "IPv6" in si.enabled_afi: ipv6.update([IP.prefix(ip).first for ip in si.ipv6_addresses]) p = [str(x.first) for x in sorted(ipv4)] p += [str(x.first) for x in sorted(ipv6)] return p def field_interfaces_count(self, obj): return self.get_vc_interfaces_count(obj.id) def field_prefixes(self, obj): p = self.get_vc_prefixes(obj.id) if p: return ", ".join(p) else: return "-" def field_row_class(self, o): return o.style.css_class_name if o.style else "" @view( url="^find_free/$", method=["GET"], access="read", api=True, validate={"vc_domain": ModelParameter(VCDomain), "vc_filter": ModelParameter(VCFilter)}, ) def api_find_free(self, request, vc_domain, vc_filter, **kwargs): return vc_domain.get_free_label(vc_filter) @view( url="^bulk/import/", method=["POST"], access="import", api=True, validate={ "vc_domain": ModelParameter(VCDomain), "items": ListOfParameter( element=DictParameter( attrs={ "l1": IntParameter(), "l2": IntParameter(), "name": StringParameter(), "description": StringParameter(default=""), } ) ), }, ) def api_bulk_import(self, request, vc_domain, items): n = 0 for i in items: if not VC.objects.filter(vc_domain=vc_domain, l1=i["l1"], l2=i["l2"]).exists(): # Add only not-existing VC( vc_domain=vc_domain, l1=i["l1"], l2=i["l2"], name=i["name"], description=i["description"], ).save() n += 1 return {"status": True, "imported": n} @view(url=r"^(?P<vc_id>\d+)/interfaces/$", method=["GET"], access="read", api=True) def api_interfaces(self, request, vc_id): """ Returns a dict of {untagged: ..., tagged: ...., l3: ...} :param request: :param vc_id: :return: """ vc = self.get_object_or_404(VC, id=int(vc_id)) l1 = vc.l1 # Managed objects in VC domain objects = set(vc.vc_domain.managedobject_set.values_list("id", flat=True)) # Find untagged interfaces si_objects = defaultdict(list) for si in SubInterface.objects.filter( managed_object__in=objects, untagged_vlan=l1, enabled_afi="BRIDGE" ): si_objects[si.managed_object] += [{"name": si.name}] untagged = [ { "managed_object_id": o.id, "managed_object_name": o.name, "interfaces": sorted(si_objects[o], key=lambda x: x["name"]), } for o in si_objects ] # Find tagged interfaces si_objects = defaultdict(list) for si in SubInterface.objects.filter( managed_object__in=objects, tagged_vlans=l1, enabled_afi="BRIDGE" ): si_objects[si.managed_object] += [{"name": si.name}] tagged = [ { "managed_object_id": o.id, "managed_object_name": o.name, "interfaces": sorted(si_objects[o], key=lambda x: x["name"]), } for o in si_objects ] # Find l3 interfaces si_objects = defaultdict(list) for si in SubInterface.objects.filter(managed_object__in=objects, vlan_ids=l1): si_objects[si.managed_object] += [ { "name": si.name, "ipv4_addresses": si.ipv4_addresses, "ipv6_addresses": si.ipv6_addresses, } ] l3 = [ { "managed_object_id": o.id, "managed_object_name": o.name, "interfaces": sorted(si_objects[o], key=lambda x: x["name"]), } for o in si_objects ] # Update caches ic = sum(len(x["interfaces"]) for x in untagged) ic += sum(len(x["interfaces"]) for x in tagged) ic += sum(len(x["interfaces"]) for x in l3) # return { "untagged": sorted(untagged, key=lambda x: x["managed_object_name"]), "tagged": sorted(tagged, key=lambda x: x["managed_object_name"]), "l3": sorted(l3, key=lambda x: x["managed_object_name"]), }
class AlarmApplication(ExtApplication): """ fm.alarm application """ title = "Alarm" menu = "Alarms" glyph = "exclamation-triangle" implied_permissions = { "launch": ["sa:managedobject:alarm"] } model_map = { "A": ActiveAlarm, "C": ArchivedAlarm } clean_fields = { "managed_object": ModelParameter(ManagedObject), "timestamp": DateTimeParameter() } ignored_params = ["status", "_dc"] def __init__(self, *args, **kwargs): ExtApplication.__init__(self, *args, **kwargs) from plugins.base import AlarmPlugin # Load plugins self.plugins = {} for f in os.listdir("fm/apps/alarm/plugins/"): if (not f.endswith(".py") or f == "base.py" or f.startswith("_")): continue mn = "noc.fm.apps.alarm.plugins.%s" % f[:-3] m = __import__(mn, {}, {}, "*") for on in dir(m): o = getattr(m, on) if (inspect.isclass(o) and issubclass(o, AlarmPlugin) and o.__module__.startswith(mn)): assert o.name self.plugins[o.name] = o(self) def cleaned_query(self, q): q = q.copy() for p in self.ignored_params: if p in q: del q[p] for p in ( self.limit_param, self.page_param, self.start_param, self.format_param, self.sort_param, self.query_param, self.only_param): if p in q: del q[p] # Normalize parameters for p in q: qp = p.split("__")[0] if qp in self.clean_fields: q[p] = self.clean_fields[qp].clean(q[p]) if "administrative_domain" in q: a = AdministrativeDomain.objects.get(id = q["administrative_domain"]) q["managed_object__in"] = a.managedobject_set.values_list("id", flat=True) q.pop("administrative_domain") if "managedobjectselector" in q: s = SelectorCache.objects.filter(selector = q["managedobjectselector"]).values_list("object") if "managed_object__in" in q: q["managed_object__in"] = list(set(q["managed_object__in"]).intersection(s)) else: q["managed_object__in"] = s q.pop("managedobjectselector") # if "collapse" in q: c = q["collapse"] del q["collapse"] if c != "0": q["root__exists"] = False return q def instance_to_dict(self, o, fields=None): s = AlarmSeverity.get_severity(o.severity) n_events = (ActiveEvent.objects.filter(alarms=o.id).count() + ArchivedEvent.objects.filter(alarms=o.id).count()) d = { "id": str(o.id), "status": o.status, "managed_object": o.managed_object.id, "managed_object__label": o.managed_object.name, "administrative_domain": o.managed_object.administrative_domain_id, "administrative_domain__label": o.managed_object.administrative_domain.name, "severity": o.severity, "severity__label": s.name, "alarm_class": str(o.alarm_class.id), "alarm_class__label": o.alarm_class.name, "timestamp": self.to_json(o.timestamp), "subject": o.subject, "events": n_events, "duration": o.duration, "row_class": s.style.css_class_name } if fields: d = dict((k, d[k]) for k in fields) return d def queryset(self, request, query=None): """ Filter records for lookup """ status = request.GET.get("status", "A") if status not in self.model_map: raise Exception("Invalid status") model = self.model_map[status] return model.objects.all() @view(url=r"^$", access="launch", method=["GET"], api=True) def api_list(self, request): return self.list_data(request, self.instance_to_dict) @view(url=r"^(?P<id>[a-z0-9]{24})/$", method=["GET"], api=True, access="launch") def api_alarm(self, request, id): alarm = get_alarm(id) if not alarm: self.response_not_found() user = request.user lang = "en" d = self.instance_to_dict(alarm) d["body"] = alarm.body d["symptoms"] = alarm.alarm_class.symptoms d["probable_causes"] = alarm.alarm_class.probable_causes d["recommended_actions"] = alarm.alarm_class.recommended_actions d["vars"] = sorted(alarm.vars.items()) d["status"] = alarm.status d["status__label"] = { "A": "Active", "C": "Cleared" }[alarm.status] # Managed object properties mo = alarm.managed_object d["managed_object_address"] = mo.address d["managed_object_profile"] = mo.profile_name d["managed_object_platform"] = mo.platform d["managed_object_version"] = mo.get_attr("version") # Log if alarm.log: d["log"] = [ { "timestamp": self.to_json(l.timestamp), "from_status": l.from_status, "to_status": l.to_status, "message": l.message } for l in alarm.log ] # Events events = [] for ec in ActiveEvent, ArchivedEvent: for e in ec.objects.filter(alarms=alarm.id): events += [{ "id": str(e.id), "event_class": str(e.event_class.id), "event_class__label": e.event_class.name, "timestamp": self.to_json(e.timestamp), "status": e.status, "managed_object": e.managed_object.id, "managed_object__label": e.managed_object.name, "subject": e.subject }] if events: d["events"] = events # Alarms children = self.get_nested_alarms(alarm) if children: d["alarms"] = { "expanded": True, "children": children } # Subscribers if alarm.status == "A": d["subscribers"] = self.get_alarm_subscribers(alarm) d["is_subscribed"] = user in alarm.subscribers # Apply plugins if alarm.alarm_class.plugins: plugins = [] for p in alarm.alarm_class.plugins: if p.name in self.plugins: plugin = self.plugins[p.name] dd = plugin.get_data(alarm, p.config) if "plugins" in dd: plugins += dd["plugins"] del dd["plugins"] d.update(dd) if plugins: d["plugins"] = plugins return d def get_alarm_subscribers(self, alarm): """ JSON-serializable subscribers :param alarm: :return: """ subscribers = [] for u in alarm.subscribers: try: u = User.objects.get(id=u) subscribers += [{ "id": u.id, "name": " ".join([u.first_name, u.last_name]), "login": u.username }] except User.DoesNotExist: pass return subscribers def get_nested_alarms(self, alarm): """ Return nested alarms as a part of NodeInterface :param alarm: :return: """ children = [] for ac in (ActiveAlarm, ArchivedAlarm): for a in ac.objects.filter(root=alarm.id): s = AlarmSeverity.get_severity(a.severity) c = { "id": str(a.id), "subject": a.subject, "alarm_class": str(a.alarm_class.id), "alarm_class__label": a.alarm_class.name, "managed_object": a.managed_object.id, "managed_object__label": a.managed_object.name, "timestamp": self.to_json(a.timestamp), "iconCls": "icon_error", "row_class": s.style.css_class_name } nc = self.get_nested_alarms(a) if nc: c["children"] = nc c["expanded"] = True else: c["leaf"] = True children += [c] return children @view(url=r"^(?P<id>[a-z0-9]{24})/post/", method=["POST"], api=True, access="launch", validate={"msg": UnicodeParameter()}) def api_post(self, request, id, msg): alarm = get_alarm(id) if not alarm: self.response_not_found() alarm.log_message("%s: %s" % (request.user.username, msg)) return True @view(url=r"^(?P<id>[a-z0-9]{24})/subscribe/", method=["POST"], api=True, access="launch") def api_subscribe(self, request, id): alarm = get_alarm(id) if not alarm: return self.response_not_found() if alarm.status == "A": alarm.subscribe(request.user) return self.get_alarm_subscribers(alarm) else: return [] @view(url=r"^(?P<id>[a-z0-9]{24})/unsubscribe/", method=["POST"], api=True, access="launch") def api_unsubscribe(self, request, id): alarm = get_alarm(id) if not alarm: return self.response_not_found() if alarm.status == "A": alarm.unsubscribe(request.user) return self.get_alarm_subscribers(alarm) else: return [] @view(url=r"^(?P<id>[a-z0-9]{24})/clear/", method=["POST"], api=True, access="launch") def api_clear(self, request, id): alarm = get_alarm(id) if alarm.status == "A": alarm.clear_alarm("Cleared by %s" % request.user) return True @view(url=r"^(?P<id>[a-z0-9]{24})/set_root/", method=["POST"], api=True, access="launch", validate={"root": StringParameter()}) def api_set_root(self, request, id, root): alarm = get_alarm(id) r = get_alarm(root) if not r: return self.response_not_found() alarm.set_root(r) return True
class ProbeApplication(ExtDocApplication): """ PMProbe application """ title = "Probe" menu = "Setup | Probes" model = Probe query_fields = ["name"] REFRESH_CHUNK = config.getint("pm", "expired_refresh_chunk") REFRESH_TIMEOUT = config.getint("pm", "expired_refresh_timeout") @view(url="^(?P<name>[^/]+)/(?P<instance>\d+)/config/$", method=["GET"], validate={"last": DateTimeParameter(required=False)}, access="config", api=True) def api_config(self, request, name, instance, last=None): """ Get full probe configuration """ probe = self.get_object_or_404(Probe, name=name) if not probe.user or request.user.id != probe.user.id: return self.response_forbidden() instance = int(instance) if instance >= probe.n_instances: return self.response_not_found("Invalid instance") probe_id = str(probe.id) now = datetime.datetime.now() # Refresh expired congfigs t0 = time.time() nr = 0 dt = 0 stopped = False for pc in ProbeConfig.objects.filter(probe_id=probe_id, instance_id=instance, expire__lt=now): pc.refresh() nr += 1 if nr % self.REFRESH_CHUNK: # Check execution time dt = time.time() - t0 if dt > self.REFRESH_TIMEOUT: self.logger.info( "%d configs has been refreshed in %s seconds. Giving up", nr, dt) stopped = True break if nr and not stopped: self.logger.info("%d configs has been refreshed in %s seconds.", nr, dt) # Get configs q = {"probe_id": probe_id, "instance_id": instance} if last: fmt = "%Y-%m-%dT%H:%M:%S.%f" if "." in last else "%Y-%m-%dT%H:%M:%S" last = datetime.datetime.strptime(last, fmt) q["changed"] = {"$gte": last} config = [{ "uuid": pc["uuid"], "handler": pc["handler"], "interval": pc["interval"], "metrics": [{ "metric": m["metric"], "metric_type": m["metric_type"], "thresholds": m["thresholds"], "convert": m["convert"], "scale": m["scale"], "collectors": { "policy": m["collectors"]["policy"], "write_concern": m["collectors"]["write_concern"], "collectors": [{ "proto": c["proto"], "address": c["address"], "port": c["port"] } for c in m["collectors"]["collectors"]] } } for m in pc["metrics"]], "config": pc["config"], "managed_object": pc.get("managed_object", None), "changed": pc["changed"].isoformat(), "expire": pc["expire"].isoformat() } for pc in ProbeConfig._get_collection().find(q)] if config: expire = min(c["expire"] for c in config) # Wipe out deleted configs deleted = [ c["uuid"] for c in config if c["changed"] == c["expire"] ] if deleted: ProbeConfig.objects.filter(uuid__in=deleted).delete() else: expire = None return { "now": now.isoformat(), "last": last.isoformat() if last else None, "expire": expire, "config": config } @view(url="^(?P<name>[^/]+)/(?P<instance>\d+)/feed/$", method=["POST"], validate={ "thresholds": DictListParameter( attrs={ "managed_object": IntParameter(), "metric": StringParameter(), "metric_type": StringParameter(), "ts": IntParameter(), "value": FloatParameter(), "old_state": StringParameter(), "new_state": StringParameter() }) }, access="config", api=True) def api_fmfeed(self, request, name, instance, thresholds): if thresholds: cnt = itertools.count() batch = NewEvent._get_collection().initialize_unordered_bulk_op() for t in thresholds: seq = struct.pack("!II", int(time.time()), cnt.next() & 0xFFFFFFFFL) batch.insert({ "timestamp": datetime.datetime.fromtimestamp(t["ts"]), "managed_object": t["managed_object"], "raw_vars": { "source": "system", "metric": t["metric"], "metric_type": t["metric_type"], "value": str(t["value"]), "old_state": t["old_state"], "new_state": t["new_state"] }, "seq": Binary(seq) }) batch.execute(0)