def api_action_group_edit(self, request): validator = DictParameter( attrs={"ids": ListOfParameter(element=ModelParameter(self.model), convert=True)} ) rv = self.deserialize(request.body) try: v = validator.clean(rv) except InterfaceTypeError as e: self.logger.info("Bad request: %r (%s)", request.body, e) return self.render_json( {"status": False, "message": "Bad request", "traceback": str(e)}, status=self.BAD_REQUEST, ) objects = v["ids"] del v["ids"] try: v = self.clean(v) except ValueError as e: return self.render_json( {"status": False, "message": "Bad request", "traceback": str(e)}, status=self.BAD_REQUEST, ) for o in objects: for p in v: setattr(o, p, v[p]) o.save() return "%d records has been updated" % len(objects)
def clean_config(self, handler): v = {} if isinstance(handler.config, dict): ci = DictParameter(attrs=handler.config) else: ci = handler.config # Read config for opt in self.config.options(handler.name): if opt not in ("enabled", "handler"): v[opt] = self.config.get(handler.name, opt) # Clean config return ci.clean(v)
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 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 decorate(f): f.url = url f.url_name = url_name # Process access if isinstance(access, bool): f.access = Permit() if access else Deny() elif isinstance(access, six.string_types): f.access = HasPerm(access) else: f.access = access f.menu = menu f.method = method f.api = api if isinstance(validate, dict): f.validate = DictParameter(attrs=validate) else: f.validate = validate return f
) from noc.sa.models.managedobject import ManagedObject 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
def test_dict_parameter_error(raw, config): with pytest.raises(InterfaceTypeError): assert DictParameter(**config).clean(raw)
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)
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(
def test_dict_parameter(raw, config, expected): assert DictParameter(**config).clean(raw) == expected
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"]), }
from noc.aaa.models.group import Group from noc.pm.models.metricscope import MetricScope from noc.pm.models.metrictype import MetricType from noc.bi.models.dashboard import Dashboard, DashboardAccess, DAL_ADMIN, DAL_RO from noc.sa.interfaces.base import DictListParameter, DictParameter, IntParameter, StringParameter from noc.core.perf import metrics from noc.core.translation import ugettext as _ # Access items validations I_VALID = DictListParameter( attrs={ "group": DictParameter( attrs={ "id": IntParameter(required=True), "name": StringParameter(required=False) }, required=False, ), "user": DictParameter( attrs={ "id": IntParameter(required=True), "name": StringParameter(required=False) }, required=False, ), "level": IntParameter(min_value=-1, max_value=3, default=-1), })
class ObjectListApplication(ExtApplication): """ ManagedObject application """ model = ManagedObject # Default filter by is_managed managed_filter = True def queryset(self, request, query=None): """ Filter records for lookup """ self.logger.info("Queryset %s" % query) if self.managed_filter: q = d_Q(is_managed=True) else: q = d_Q() if not request.user.is_superuser: q &= UserAccess.Q(request.user) if query: sq = ManagedObject.get_search_Q(query) if sq: q &= sq else: q &= d_Q(name__contains=query) return self.model.objects.filter(q) def instance_to_dict(self, o, fields=None): return { "id": str(o.id), "name": o.name, "address": o.address, "profile_name": o.profile.name, "platform": o.platform.name if o.platform else "", "version": o.version.version if o.version else "", "row_class": o.object_profile.style.css_class_name if o.object_profile.style else "" # "row_class": "" } def cleaned_query(self, q): nq = {} for k in q: if not k.startswith("_") and "__" not in k: nq[k] = q[k] ids = set() self.logger.debug("Cleaned query: %s" % nq) if "ids" in nq: ids = {int(nid) for nid in nq["ids"]} del nq["ids"] if "administrative_domain" in nq: ad = AdministrativeDomain.get_nested_ids( int(nq["administrative_domain"])) if ad: del nq["administrative_domain"] nq["administrative_domain__in"] = ad if "selector" in nq: s = self.get_object_or_404(ManagedObjectSelector, id=int(q["selector"])) if s: if ids: # nq["id__in"] = set(ManagedObject.objects.filter(s.Q).values_list("id", flat=True)) ids = ids.intersection( set( ManagedObject.objects.filter(s.Q).values_list( "id", flat=True))) else: ids = set( ManagedObject.objects.filter(s.Q).values_list( "id", flat=True)) del nq["selector"] mq = None c_in = [] c_nin = [] for cc in [part for part in nq if part.startswith("caps")]: """ Caps: caps0=CapsID,caps1=CapsID:true.... cq - caps query mq - main_query caps0=CapsID - caps is exists caps0=!CapsID - caps is not exists caps0=CapsID:true - caps value equal True caps0=CapsID:2~50 - caps value many then 2 and less then 50 c_ids = set(ObjectCapabilities.objects(cq).distinct('object')) """ # @todo Убирать дубликаты (повторно не добавлять) c = nq.pop(cc) if not c: continue if not mq: mq = m_Q() self.logger.info("Caps: %s" % c) if "!" in c: # @todo Добавить исключение (только этот) !ID c_id = c[1:] c_query = "nexists" elif ":" not in c: c_id = c c_query = "exists" else: c_id, c_query = c.split(":", 1) try: c_id = bson.ObjectId(c_id) except bson.errors.InvalidId as e: self.logger.warning(e) continue if "~" in c_query: l, r = c_query.split("~") if not l: cond = {"$lte": int(r)} elif not r: cond = {"$gte": int(l)} else: cond = {"$lte": int(r), "$gte": int(l)} cq = m_Q(__raw__={ "caps": { "$elemMatch": { "capability": c_id, "value": cond } } }) elif c_query in ("false", "true"): cq = m_Q(caps__match={ "capability": c_id, "value": c_query == "true" }) elif c_query == "exists": c_in += [c_id] continue elif c_query == "nexists": c_nin += [c_id] continue else: try: c_query = int(c_query) cq = m_Q( __raw__={ "caps": { "$elemMatch": { "capability": c_id, "value": int(c_query) } } }) except ValueError: cq = m_Q( __raw__={ "caps": { "$elemMatch": { "capability": c_id, "value": { "$regex": c_query } } } }) mq &= cq if c_in: mq &= m_Q(caps__capability__in=c_in) if c_nin: mq &= m_Q(caps__capability__nin=c_nin) if mq: c_ids = set(el["_id"] for el in ObjectCapabilities.objects( mq).values_list("object").as_pymongo()) self.logger.info("Caps objects count: %d" % len(c_ids)) ids = ids.intersection(c_ids) if ids else c_ids if "addresses" in nq: if isinstance(nq["addresses"], list): nq["address__in"] = nq["addresses"] else: nq["address__in"] = [nq["addresses"]] del nq["addresses"] if ids: nq["id__in"] = list(ids) xf = list((set(nq.keys())) - set(f.name for f in self.model._meta.get_fields())) # @todo move validation fields for x in xf: if x in ["address__in", "id__in", "administrative_domain__in"]: continue self.logger.warning("Remove element not in model: %s" % x) del nq[x] return nq def extra_query(self, q, order): extra = {"select": {}} if "address" in order: extra["select"]["ex_address"] = " cast_test_to_inet(address) " extra["order_by"] = ["ex_address", "address"] elif "-address" in order: extra["select"]["ex_address"] = " cast_test_to_inet(address) " extra["order_by"] = ["-ex_address", "-address"] self.logger.info("Extra: %s" % extra) return extra, [] if "order_by" in extra else order @view(method=["GET", "POST"], url="^$", access="read", api=True) def api_list(self, request): return self.list_data(request, self.instance_to_dict) @view( method=["POST"], url="^iplist/$", access="launch", api=True, validate={ "query": DictParameter( attrs={ "addresses": ListOfParameter(element=IPv4Parameter(), convert=True) }) }, ) def api_action_ip_list(self, request, query): # @todo Required user vault implementation return self.render_json({"status": True})
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 try: profile = user.get_profile() language = profile.preferred_language theme = profile.theme preview_theme = profile.preview_theme 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 profile.userprofilecontact_set.all()] except UserProfile.DoesNotExist: language = None theme = None preview_theme = None contacts = [] 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", "theme": theme or "gray", "preview_theme": preview_theme or "midnight", "contacts": contacts } @view(url="^$", method=["POST"], access=PermitLogged(), api=True, validate={ "preferred_language": StringParameter(choices=[x[0] for x in LANGUAGES]), "theme": StringParameter(), "preview_theme": StringParameter(), "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, theme, preview_theme, contacts): user = request.user try: profile = user.get_profile() except UserProfile.DoesNotExist: profile = UserProfile(user=user) profile.preferred_language = preferred_language profile.theme = theme profile.preview_theme = preview_theme profile.save() # Setup contacts for c in profile.userprofilecontact_set.all(): c.delete() for c in contacts: UserProfileContact(user_profile=profile, time_pattern=c["time_pattern"], notification_method=c["notification_method"], params=c["params"]).save() # Setup language request.session["django_lang"] = preferred_language return True
# Python modules from __future__ import absolute_import # Third-party modules import tornado.gen import ujson import six # NOC modules from noc.core.service.apiaccess import authenticated from noc.sa.interfaces.base import DictParameter, StringListParameter from noc.sa.models.objectstatus import ObjectStatus from ..base import NBIAPI Request = DictParameter(attrs={"objects": StringListParameter(required=True)}) class ObjectStatusAPI(NBIAPI): name = "objectstatus" @authenticated @tornado.gen.coroutine def post(self): code, result = yield self.executor.submit(self.handler) self.set_status(code) if isinstance(result, six.string_types): self.write(result) else: self.write(ujson.dumps(result))
class RunCommandsApplication(ExtApplication): title = _("Run Commands") menu = [_("Run Commands")] implied_permissions = {"launch": ["sa:objectlist:read"]} @view(url=r"^form/snippet/(?P<snippet_id>\d+)/$", method=["GET"], access="launch", api=True) def api_form_snippet(self, request, snippet_id): snippet = self.get_object_or_404(CommandSnippet, id=int(snippet_id)) r = [] vars = snippet.vars for k in vars: cfg = { "name": k, "fieldLabel": k, "allowBlank": not vars[k].get("required", False) } t = vars[k].get("type") if t == "int": cfg["xtype"] = "numberfield" else: cfg["xtype"] = "textfield" r += [cfg] return r @view(url=r"^form/action/(?P<action_id>[0-9a-f]{24})/$", method=["GET"], access="launch", api=True) def api_form_action(self, request, action_id): action = self.get_object_or_404(Action, id=action_id) r = [] for p in action.params: cfg = { "name": p.name, "fieldLabel": p.description or p.name, "allowBlank": not p.is_required, } if p.type == "int": cfg["xtype"] = "numberfield" else: cfg["xtype"] = "textfield" r += [cfg] return r @view( url=r"^render/snippet/(?P<snippet_id>\d+)/$", method=["POST"], validate={ "objects": ListOfParameter(element=ModelParameter(ManagedObject)), "config": DictParameter(), }, access="launch", api=True, ) def api_render_snippet(self, request, snippet_id, objects, config): snippet = self.get_object_or_404(CommandSnippet, id=int(snippet_id)) r = {} for mo in objects: config["object"] = mo r[mo.id] = snippet.expand(config) return r @view( url=r"^render/action/(?P<action_id>[0-9a-f]{24})/$", method=["POST"], validate={ "objects": ListOfParameter(element=ModelParameter(ManagedObject)), "config": DictParameter(), }, access="launch", api=True, ) def api_render_action(self, request, action_id, objects, config): action = self.get_object_or_404(Action, id=action_id) r = {} for mo in objects: r[mo.id] = action.expand(mo, **config) return r
StringListParameter, IntParameter, ListOfParameter, ListParameter, ) from noc.pm.models.metrictype import MetricType from ..base import NBIAPI Request = DictParameter( attrs={ "bi_id": IntParameter(required=True), "metrics": DictListParameter( attrs={ "metric_type": StringParameter(required=True), "path": StringListParameter(required=True), "values": ListOfParameter(ListParameter(), required=True), }, required=True, ), } ) class TelemetryAPI(NBIAPI): name = "telemetry" @authenticated @tornado.gen.coroutine def post(self): code, result = yield self.executor.submit(self.handler)
DictParameter, DictListParameter, StringParameter, StringListParameter, IntParameter, ListOfParameter, ListParameter, ) from noc.pm.models.metrictype import MetricType from noc.services.nbi.base import NBIAPI Request = DictParameter( attrs={ "action": StringParameter(required=True), "host": StringParameter(required=True), "port": StringParameter(required=True), "sn": StringParameter(required=True), "llid": IntParameter(required=True), "login": StringParameter(required=True), "passwd": StringParameter(required=True), }) class ZTERegONU(NBIAPI): name = "zteregonu" @authenticated @tornado.gen.coroutine def post(self): connect() code, result = yield self.executor.submit(self.handler) self.set_status(code)
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): self.write(result)
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 MapApplication(ExtApplication): """ inv.net application """ title = _("Network Map") menu = _("Network Map") glyph = "globe" implied_permissions = {"launch": ["inv:networksegment:lookup"]} # Object statuses ST_UNKNOWN = 0 # Object state is unknown ST_OK = 1 # Object is OK ST_ALARM = 2 # Object is reachable, Active alarms ST_UNREACH = 3 # Object is unreachable due to other's object failure ST_DOWN = 4 # Object is down ST_MAINTENANCE = 32 # Maintenance bit @view(r"^(?P<id>[0-9a-f]{24})/data/$", method=["GET"], access="read", api=True) def api_data(self, request, id): def q_mo(d): x = d.copy() if x["type"] == "managedobject": del x["mo"] x["external"] = x["id"] not in mos if is_view else x.get( "role") != "segment" elif d["type"] == "cloud": del x["link"] x["external"] = False return x # Find segment segment = self.get_object_or_404(NetworkSegment, id=id) if segment.managed_objects.count() > segment.max_objects: # Too many objects return { "id": str(segment.id), "name": segment.name, "error": _("Too many objects") } # if we set selector in segment is_view = segment.selector if is_view: mos = segment.selector.managed_objects.values_list("id", flat=True) # Load settings settings = MapSettings.objects.filter(segment=id).first() node_hints = {} link_hints = {} if settings: self.logger.info("Using stored positions") for n in settings.nodes: node_hints[n.id] = { "type": n.type, "id": n.id, "x": n.x, "y": n.y } for ll in settings.links: link_hints[ll.id] = { "connector": ll.connector if len(ll.vertices) else "normal", "vertices": [{ "x": v.x, "y": v.y } for v in ll.vertices], } else: self.logger.info("Generating positions") # Generate topology topology = SegmentTopology( segment, node_hints, link_hints, force_spring=request.GET.get("force") == "spring") topology.layout() # Build output r = { "id": str(segment.id), "max_links": int(segment.max_shown_downlinks), "name": segment.name, "caps": list(topology.caps), "nodes": [q_mo(x) for x in topology.G.nodes.values()], "links": [topology.G[u][v] for u, v in topology.G.edges()], } # Parent info if segment.parent: r["parent"] = { "id": str(segment.parent.id), "name": segment.parent.name } # Save settings if not settings: self.logger.debug("Saving first-time layout") MapSettings.load_json({ "id": str(segment.id), "nodes": [{ "type": n["type"], "id": n["id"], "x": n["x"], "y": n["y"] } for n in r["nodes"] if n.get("x") is not None and n.get("y") is not None], "links": [{ "type": n["type"], "id": n["id"], "vertices": n.get("vertices", []), "connector": n.get("connector", "normal"), } for n in r["links"]], }) return r @view(r"^(?P<id>[0-9a-f]{24})/data/$", method=["POST"], access="write", api=True) def api_save(self, request, id): self.get_object_or_404(NetworkSegment, id=id) data = self.deserialize(request.body) data["id"] = id MapSettings.load_json(data, request.user.username) return {"status": True} @view(url=r"^(?P<id>[0-9a-f]{24})/info/segment/$", method=["GET"], access="read", api=True) def api_info_segment(self, request, id): segment = self.get_object_or_404(NetworkSegment, id=id) r = { "name": segment.name, "description": segment.description, "objects": segment.managed_objects.count(), } return r @view( url=r"^(?P<id>[0-9a-f]{24})/info/managedobject/(?P<mo_id>\d+)/$", method=["GET"], access="read", api=True, ) def api_info_managedobject(self, request, id, mo_id): segment = self.get_object_or_404(NetworkSegment, id=id) object = self.get_object_or_404(ManagedObject, id=int(mo_id)) s = {1: "telnet", 2: "ssh", 3: "http", 4: "https"}[object.scheme] r = { "id": object.id, "name": object.name, "description": object.description, "address": object.address, "platform": object.platform.full_name if object.platform else "", "profile": object.profile.name, "external": object.segment.id != segment.id, "external_segment": { "id": str(object.segment.id), "name": object.segment.name }, "caps": object.get_caps(), "console_url": "%s://%s/" % (s, object.address), } return r @view( url=r"^(?P<id>[0-9a-f]{24})/info/link/(?P<link_id>[0-9a-f]{24})/$", method=["GET"], access="read", api=True, ) def api_info_link(self, request, id, link_id): def q(s): if isinstance(s, str): s = s.encode("utf-8") return s self.get_object_or_404(NetworkSegment, id=id) link = self.get_object_or_404(Link, id=link_id) r = { "id": str(link.id), "name": link.name or None, "description": link.description or None, "objects": [], "method": link.discovery_method, } o = defaultdict(list) for i in link.interfaces: o[i.managed_object] += [i] for mo in sorted(o, key=lambda x: x.name): r["objects"] += [{ "id": mo.id, "name": mo.name, "interfaces": [{ "name": i.name, "description": i.description or None, "status": i.status } for i in sorted(o[mo], key=lambda x: alnum_key(x.name))], }] # Get link bandwidth mo_in = defaultdict(float) mo_out = defaultdict(float) mos = [ManagedObject.get_by_id(mo["id"]) for mo in r["objects"]] metric_map, last_ts = get_interface_metrics(list(o)) for mo in o: if mo not in metric_map: continue for i in o[mo]: if i.name not in metric_map[mo]: continue mo_in[mo] += metric_map[mo][i.name]["Interface | Load | In"] mo_out[mo] += metric_map[mo][i.name]["Interface | Load | Out"] if len(mos) == 2: mo1, mo2 = mos r["utilisation"] = [ int(max(mo_in[mo1], mo_out[mo2])), int(max(mo_in[mo2], mo_out[mo1])), ] else: mv = list(mo_in.values()) + list(mo_out.values()) if mv: r["utilisation"] = [int(max(mv))] else: r["utilisation"] = 0 return r @view( url=r"^(?P<id>[0-9a-f]{24})/info/cloud/(?P<link_id>[0-9a-f]{24})/$", method=["GET"], access="read", api=True, ) def api_info_cloud(self, request, id, link_id): self.get_object_or_404(NetworkSegment, id=id) link = self.get_object_or_404(Link, id=link_id) r = { "id": str(link.id), "name": link.name or None, "description": link.description or None, "objects": [], "method": link.discovery_method, } o = defaultdict(list) for i in link.interfaces: o[i.managed_object] += [i] for mo in sorted(o, key=lambda x: x.name): r["objects"] += [{ "id": mo.id, "name": mo.name, "interfaces": [{ "name": i.name, "description": i.description or None, "status": i.status } for i in sorted(o[mo], key=lambda x: alnum_key(x.name))], }] return r @view( url=r"^objects_statuses/$", method=["POST"], access="read", api=True, validate={"objects": ListOfParameter(IntParameter())}, ) def api_objects_statuses(self, request, objects: List[int]): def get_alarms(objects: List[int]) -> Set[int]: """ Returns a set of objects with alarms """ alarms: Set[int] = set() coll = ActiveAlarm._get_collection() while objects: chunk, objects = objects[:500], objects[500:] a = coll.aggregate([ { "$match": { "managed_object": { "$in": chunk } } }, { "$group": { "_id": "$managed_object", "count": { "$sum": 1 } } }, ]) alarms.update(d["_id"] for d in a) return alarms def get_maintenance(objects: List[int]) -> Set[int]: """ Returns a set of objects currently in maintenance :param objects: :return: """ now = datetime.datetime.now() so = set(objects) mnt_objects = set() pipeline = [ { "$match": { "affected_objects.object": { "$in": list(so) } } }, { "$unwind": "$affected_objects" }, { "$lookup": { "from": "noc.maintenance", "as": "m", "let": { "maintenance": "_id" }, "pipeline": [{ "$match": { "m.is_completed": False, "m.start": { "$lte": now }, "m.stop": { "gte": now }, }, }], }, }, { "$project": { "_id": 0, "object": "$affected_objects.object", } }, { "$group": { "_id": "$object" } }, ] mnt_objects |= so & { x["_id"] for x in AffectedObjects._get_collection().aggregate(pipeline) } return mnt_objects # Mark all as unknown r = {o: self.ST_UNKNOWN for o in objects} sr = ObjectStatus.get_statuses(objects) sa = get_alarms(objects) mo = get_maintenance(objects) for o in sr: if sr[o]: # Check for alarms if o in sa: r[o] = self.ST_ALARM else: r[o] = self.ST_OK else: r[o] = self.ST_DOWN if o in mo: r[o] |= self.ST_MAINTENANCE return r @classmethod @cachedmethod(key="managedobject-name-to-id-%s", lock=lambda _: tags_lock) def managedobject_name_to_id(cls, name): r = ManagedObject.objects.filter(name=name).values_list("id") if r: return r[0][0] return None @classmethod @cachedmethod(key="interface-tags-to-id-%s-%s", lock=lambda _: tags_lock) def interface_tags_to_id(cls, object_name, interface_name): mo = cls.managedobject_name_to_id(object_name) i = Interface._get_collection().find_one({ "managed_object": mo, "name": interface_name }) if i: return i["_id"] return None @view( url=r"^metrics/$", method=["POST"], access="read", api=True, validate={ "metrics": DictListParameter( attrs={ "id": StringParameter(), "metric": StringParameter(), "tags": DictParameter(), }) }, ) def api_metrics(self, request, metrics): def q(s): if isinstance(s, str): s = s.encode("utf-8") return s def qt(t): return "|".join(["%s=%s" % (v, t[v]) for v in sorted(t)]) # Build query tag_id = {} # object, interface -> id if_ids = {} # id -> port id mlst = [] # (metric, object, interface) for m in metrics: if "object" in m["tags"] and "interface" in m["tags"]: if not m["tags"]["object"]: continue try: if_ids[self.interface_tags_to_id( m["tags"]["object"], m["tags"]["interface"])] = m["id"] object = ManagedObject.objects.get( name=m["tags"]["object"]) tag_id[object, m["tags"]["interface"]] = m["id"] mlst += [(m["metric"], object, m["tags"]["interface"])] except KeyError: pass # @todo: Get last values from cache if not mlst: return {} r = {} # Apply interface statuses for d in Interface._get_collection().find( {"_id": { "$in": list(if_ids) }}, { "_id": 1, "admin_status": 1, "oper_status": 1 }): r[if_ids[d["_id"]]] = { "admin_status": d.get("admin_status", True), "oper_status": d.get("oper_status", True), } metric_map, last_ts = get_interface_metrics([m[1] for m in mlst]) # Apply metrics for rq_mo, rq_iface in tag_id: pid = tag_id.get((rq_mo, rq_iface)) if not pid: continue if pid not in r: r[pid] = {} if rq_mo not in metric_map: continue if rq_iface not in metric_map[rq_mo]: continue r[pid]["Interface | Load | In"] = metric_map[rq_mo][rq_iface][ "Interface | Load | In"] r[pid]["Interface | Load | Out"] = metric_map[rq_mo][rq_iface][ "Interface | Load | Out"] return r @view(r"^(?P<id>[0-9a-f]{24})/data/$", method=["DELETE"], access="write", api=True) def api_reset(self, request, id): self.get_object_or_404(NetworkSegment, id=id) MapSettings.objects.filter(segment=id).delete() return {"status": True} @view( url=r"^stp/status/$", method=["POST"], access="read", api=True, validate={"objects": ListOfParameter(IntParameter())}, ) def api_objects_stp_status(self, request, objects): def get_stp_status(object_id): roots = set() blocked = set() object = ManagedObject.get_by_id(object_id) sr = object.scripts.get_spanning_tree() for instance in sr["instances"]: ro = DiscoveryID.find_object(instance["root_id"]) if ro: roots.add(ro) for i in instance["interfaces"]: if i["state"] == "discarding" and i["role"] == "alternate": iface = object.get_interface(i["interface"]) if iface: link = iface.link if link: blocked.add(str(link.id)) return object_id, roots, blocked r = {"roots": [], "blocked": []} futures = [] with ThreadPoolExecutor(max_workers=10) as executor: for o in objects: futures += [executor.submit(get_stp_status, o)] for future in as_completed(futures): try: obj, roots, blocked = future.result() for ro in roots: if ro.id not in r["roots"]: r["roots"] += [ro.id] r["blocked"] += blocked except Exception as e: self.logger.error("[stp] Exception: %s", e) return r