Beispiel #1
0
class NotificationGroupApplication(ExtModelApplication):
    """
    NotificationGroup application
    """

    title = _("Notification Group")
    menu = [_("Setup"), _("Notification Groups")]
    model = NotificationGroup
    glyph = "envelope-o"

    users = ModelInline(NotificationGroupUser)
    other = ModelInline(NotificationGroupOther)

    @view(
        url="^actions/test/$",
        method=["POST"],
        access="update",
        api=True,
        validate={
            "ids": ListOfParameter(element=ModelParameter(NotificationGroup)),
            "subject": UnicodeParameter(),
            "body": UnicodeParameter(),
        },
    )
    def api_action_test(self, request, ids, subject, body):
        for g in ids:
            g.notify(subject=subject, body=body)
        return "Notification message has been sent"
Beispiel #2
0
 def init_plugin(self):
     super().init_plugin()
     self.add_view(
         "api_plugin_%s_set_contacts" % self.name,
         self.api_set_contacts,
         url="^(?P<id>[0-9a-f]{24})/plugin/%s/$" % self.name,
         method=["POST"],
         validate={
             "administrative": UnicodeParameter(),
             "billing": UnicodeParameter(),
             "technical": UnicodeParameter(),
         },
     )
Beispiel #3
0
 def init_plugin(self):
     super(CommentPlugin, self).init_plugin()
     self.add_view("api_plugin_%s_set_comment" % self.name,
                   self.api_set_comment,
                   url="^(?P<id>[0-9a-f]{24})/plugin/%s/$" % self.name,
                   method=["POST"],
                   validate={"comment": UnicodeParameter()})
Beispiel #4
0
class SearchApplication(ExtApplication):
    """
    main.search application
    """
    title = _("Search")
    menu = _("Search")
    glyph = "search noc-preview"

    @view(url="^$",
          method=["POST"],
          access="launch",
          api=True,
          validate={"query": UnicodeParameter()})
    def api_search(self, request, query):
        r = []
        for qr in TextIndex.search(query):
            model = get_model(qr["model"])
            if not model:
                continue  # Invalid model
            url = model.get_search_result_url(qr["object"])
            r += [{
                "title": str(qr["title"]),
                "card": str(qr["card"]),
                "tags:": [str(x) for x in (qr.get("tags", []) or [])],
                "url": url,
                "score": qr["score"]
            }]
        return r
Beispiel #5
0
def test_unicode_parameter():
    assert UnicodeParameter().clean(u"Test") == u"Test"
    assert UnicodeParameter().clean(10) == u"10"
    assert UnicodeParameter().clean(None) == u"None"
    assert UnicodeParameter(default=u"test").clean(u"no test") == u"no test"
    assert UnicodeParameter(default=u"test").clean(None) == u"test"
    assert UnicodeParameter(choices=[u"1", u"2"]).clean(u"1") == u"1"
    with pytest.raises(InterfaceTypeError):
        assert UnicodeParameter(choices=[u"1", u"2"]).clean(u"3")
Beispiel #6
0
 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()
                   })
Beispiel #7
0
class PrefixListBuilderApplication(ExtApplication):
    """
    Interactive prefix list builder
    """
    title = "Prefix List Builder"
    menu = "Prefix List Builder"
    # implied_permissions = {
    #    "read": ["peer:peeringpoint:lookup"]
    #}

    @view(method=["GET"],
          url=r"^$",
          access="read",
          api=True,
          validate={
              "peering_point": ModelParameter(PeeringPoint),
              "name": UnicodeParameter(required=False),
              "as_set": UnicodeParameter()
          })
    def api_list(self, request, peering_point, name, as_set):
        prefixes = WhoisCache.resolve_as_set_prefixes_maxlen(as_set)
        pl = peering_point.profile.generate_prefix_list(name, prefixes)
        return {"name": name, "prefix_list": pl, "success": True}
Beispiel #8
0
    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(),
            },
        )
Beispiel #9
0
class SearchApplication(ExtApplication):
    """
    main.search application
    """
    title = "Search"
    menu = "Search"
    INDEX = "local/index"
    LIMIT = 1000
    glyph = "search noc-preview"

    @view(url="^$", method=["POST"], access="launch", api=True,
          validate={
              "query": UnicodeParameter()
          })
    def api_search(self, request, query):
        user = request.user
        index = open_dir(self.INDEX, readonly=True)
        parser = QueryParser("content", index.schema)
        r = []
        q = parser.parse(query)
        with index.searcher() as searcher:
            for hit in searcher.search(q, limit=self.LIMIT):
                o = FTSQueue.get_object(hit["id"])
                if not o:
                    continue  # Not found in database
                li = o.get_search_info(user)
                if not li:
                    continue  # Not accessible for user
                r += [{
                    "id": hit["id"],
                    "title": hit["title"],
                    "card": hit["card"],
                    "tags": hit.get("tags"),
                    "info": li
                }]
        return r
Beispiel #10
0
class EventApplication(ExtApplication):
    """
    fm.event application
    """

    title = _("Events")
    menu = _("Events")
    icon = "icon_find"

    model_map = {"A": ActiveEvent, "F": FailedEvent, "S": ArchivedEvent}

    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 EventPlugin

        # Load plugins
        self.plugins = {}
        for f in os.listdir("services/web/apps/fm/event/plugins/"):
            if not f.endswith(".py") or f == "base.py" or f.startswith("_"):
                continue
            mn = "noc.services.web.apps.fm.event.plugins.%s" % f[:-3]
            m = __import__(mn, {}, {}, "*")
            for on in dir(m):
                o = getattr(m, on)
                if (
                    inspect.isclass(o)
                    and issubclass(o, EventPlugin)
                    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")
        return q

    def instance_to_dict(self, o, fields=None):
        row_class = None
        if o.status in ("A", "S"):
            subject = o.subject
            repeats = o.repeats
            duration = o.duration
            n_alarms = len(o.alarms)
            if n_alarms:
                row_class = AlarmSeverity.get_severity_css_class_name(get_severity(o.alarms))
        else:
            subject = None
            repeats = None
            duration = None
            n_alarms = None
        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,
            "event_class": str(o.event_class.id) if o.status in ("A", "S") else None,
            "event_class__label": o.event_class.name if o.status in ("A", "S") else None,
            "timestamp": self.to_json(o.timestamp),
            "subject": subject,
            "repeats": repeats,
            "duration": duration,
            "alarms": n_alarms,
            "row_class": row_class,
        }
        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

    @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_event(self, request, id):
        event = get_event(id)
        if not event:
            return self.response_not_found()
        d = self.instance_to_dict(event)
        dd = dict(
            (v, None)
            for v in (
                "body",
                "symptoms",
                "probable_causes",
                "recommended_actions",
                "log",
                "vars",
                "resolved_vars",
                "raw_vars",
            )
        )
        if event.status in ("A", "S"):
            dd["body"] = event.body
            dd["symptoms"] = event.event_class.symptoms
            dd["probable_causes"] = event.event_class.probable_causes
            dd["recommended_actions"] = event.event_class.recommended_actions
            # Fill vars
            left = set(event.vars)
            vars = []
            for ev in event.event_class.vars:
                if ev.name in event.vars:
                    vars += [(ev.name, event.vars[ev.name], ev.description)]
                    left.remove(ev.name)
            vars += [(v, event.vars[v], None) for v in sorted(left)]
            dd["vars"] = vars
            # Fill resolved vars
            vars = []
            is_trap = event.raw_vars.get("source") == "SNMP Trap"
            for v in sorted(event.resolved_vars):
                desc = None
                if is_trap and "::" in v:
                    desc = MIB.get_description(v)
                vars += [(v, event.resolved_vars[v], desc)]
            dd["resolved_vars"] = vars
        dd["raw_vars"] = sorted(event.raw_vars.items())
        # Managed object properties
        mo = event.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["tags"] = mo.tags
        # Log
        if event.log:
            dd["log"] = [
                {
                    "timestamp": self.to_json(l.timestamp),
                    "from_status": l.from_status,
                    "to_status": l.to_status,
                    "message": l.message,
                }
                for l in event.log
            ]
        #
        d.update(dd)
        # Get alarms
        if event.status in ("A", "S"):
            alarms = []
            for a_id in event.alarms:
                a = get_alarm(a_id)
                if not a:
                    continue
                if a.opening_event == event.id:
                    role = "O"
                elif a.closing_event == event.id:
                    role = "C"
                else:
                    role = ""
                alarms += [
                    {
                        "id": str(a.id),
                        "status": a.status,
                        "alarm_class": str(a.alarm_class.id),
                        "alarm_class__label": a.alarm_class.name,
                        "subject": a.subject,
                        "role": role,
                        "timestamp": self.to_json(a.timestamp),
                    }
                ]
            d["alarms"] = alarms
        # Apply plugins
        if event.status in ("A", "S") and event.event_class.plugins:
            plugins = []
            for p in event.event_class.plugins:
                if p.name in self.plugins:
                    plugin = self.plugins[p.name]
                    dd = plugin.get_data(event, p.config)
                    if "plugins" in dd:
                        plugins += dd["plugins"]
                        del dd["plugins"]
                    d.update(dd)
            if plugins:
                d["plugins"] = plugins
        elif event.status == "F":
            # Enable traceback plugin for failed events
            d["traceback"] = event.traceback
            d["plugins"] = [("NOC.fm.event.plugins.Traceback", {})]
        return d

    @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):
        event = get_event(id)
        if not event:
            self.response_not_found()
        event.log_message("%s: %s" % (request.user.username, msg))
        return True

    rx_parse_log = re.compile("^Classified as '(.+?)'.+$")

    @view(url=r"^(?P<id>[a-z0-9]{24})/json/$", method=["GET"], api=True, access="launch")
    def api_json(self, request, id):
        event = get_event(id)
        if not event:
            self.response_not_found()
        # Get event class
        e_class = None
        if event.status in ("A", "S"):
            for l in event.log:
                match = self.rx_parse_log.match(l.message)
                if match:
                    e_class = match.group(1)
        r = ["["]
        r += ["    {"]
        r += ['        "profile": "%s",' % json_escape(event.managed_object.profile.name)]
        if e_class:
            r += ['        "event_class__name": "%s",' % e_class]
        r += ['        "raw_vars": {']
        rr = []
        for k in event.raw_vars:
            if k in ("collector", "severity", "facility"):
                continue
            rr += ['            "%s": "%s"' % (json_escape(k), json_escape(str(event.raw_vars[k])))]
        r += [",\n".join(rr)]
        r += ["        }"]
        r += ["    }"]
        r += ["]"]
        return "\n".join(r)

    @view(url=r"^(?P<id>[a-z0-9]{24})/reclassify/$", method=["POST"], api=True, access="launch")
    def api_reclassify(self, request, id):
        event = get_event(id)
        if not event:
            self.response_not_found()
        if event.status == "N":
            return False
        event.mark_as_new(
            "Event reclassification has been requested " "by user %s" % request.user.username
        )
        return True
Beispiel #11
0
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
Beispiel #12
0
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
Beispiel #13
0
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={})

    advanced_filter_params = {
        "service_profile": "total_services",
        "subscribers_profile": "total_subscribers",
        "profile": get_advanced_field,
    }

    DEFAULT_ARCH_ALARM = datetime.timedelta(
        seconds=config.web.api_arch_alarm_limit)

    rx_oper_splitter = re.compile(r"^(?P<field>\S+)(?P<f_num>\d+)__in")

    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]
        # Extract IN
        # extjs not working with same parameter name in query
        for p in list(q):
            if p.endswith("__in") and self.rx_oper_splitter.match(p):
                field = self.rx_oper_splitter.match(p).group("field") + "__in"
                if field not in q:
                    q[field] = [q[p]]
                else:
                    q[field] += [q[p]]
                del q[p]
        # Normalize parameters
        for p in list(q):
            qp = p.split("__")[0]
            if qp in self.clean_fields:
                q[p] = self.clean_fields[qp].clean(q[p])
        # Advanced filter
        for p in self.advanced_filter_params:
            params = []
            for x in list(q):
                if x.startswith(p):
                    params += [q[x]]
                    del q[x]
            if params:
                af = self.advanced_filter(self.advanced_filter_params[p],
                                          params)
                if "__raw__" in q and "__raw__" in af:
                    # Multiple raw query
                    q["__raw__"].update(af["__raw__"])
                    del af["__raw__"]
                q.update(af)
        # Exclude maintenance
        if "maintenance" not in q:
            q["maintenance"] = "hide"
        if q["maintenance"] == "hide" and status == "A":
            q["managed_object__nin"] = Maintenance.currently_affected()
        elif q["maintenance"] == "only" and status == "A":
            q["managed_object__in"] = Maintenance.currently_affected()
        del q["maintenance"]
        if "administrative_domain" in q:
            if q["administrative_domain"] != "_root_":
                q["adm_path"] = int(q["administrative_domain"])
            q.pop("administrative_domain")
        if "administrative_domain__in" in q:
            if "_root_" not in q["administrative_domain__in"]:
                q["adm_path__in"] = q["administrative_domain__in"]
            q.pop("administrative_domain__in")
        if "segment" in q:
            if q["segment"] != "_root_":
                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 advanced_filter(self, field, params):
        """
        Field: field0=ProfileID,field1=ProfileID:true....
        cq - caps query
        mq - main_query
        field0=ProfileID - Profile is exists
        field0=!ProfileID - Profile is not exists
        field0=ProfileID:true - Summary value equal True
        field0=ProfileID:2~50 - Summary value many then 2 and less then 50

        :param field: Query Field name
        :param params: Query params
        :return:
        """
        q = {}
        c_in = []
        c_nin = []
        for c in params:
            if not c:
                continue
            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:
                if callable(field):
                    field, c_id = field(c_id)
                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)}
                q["__raw__"] = {
                    field: {
                        "$elemMatch": {
                            "profile": c_id,
                            "summary": cond
                        }
                    }
                }
            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)
                    q["__raw__"] = {
                        field: {
                            "$elemMatch": {
                                "profile": c_id,
                                "summary": int(c_query)
                            }
                        }
                    }
                except ValueError:
                    q["__raw__"] = {
                        field: {
                            "$elemMatch": {
                                "profile": c_id,
                                "summary": {
                                    "$regex": c_query
                                }
                            }
                        }
                    }
        if c_in:
            q["%s__profile__in" % field] = c_in
        if c_nin:
            q["%s__profile__nin" % field] = c_nin

        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,
            "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,
            "ack_ts":
            self.to_json(o.ack_ts),
            "ack_user":
            o.ack_user,
            "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),
            "total_subscribers":
            self.f_summary(
                {"subscriber":
                 SummaryItem.items_to_dict(o.total_subscribers)}),
            "total_services":
            self.f_summary(
                {"service": SummaryItem.items_to_dict(o.total_services)}),
            "logs": [{
                "timestamp": self.to_json(ll.timestamp),
                "user": ll.source or "NOC",
                "message": ll.message,
            } for ll in o.log if getattr(ll, "source", None)
                     ][:config.web.api_alarm_comments_limit],
        }
        if fields:
            d = {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.labels
        # Log
        if alarm.log:
            d["log"] = [{
                "timestamp": self.to_json(ll.timestamp),
                "from_status": ll.from_status,
                "to_status": ll.to_status,
                "source": getattr(ll, "source", ""),
                "message": ll.message,
            } for ll 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(msg, source=request.user.username)
        return True

    @view(
        url=r"^comment/post/",
        method=["POST"],
        api=True,
        access="launch",
        validate={
            "ids": StringListParameter(required=True),
            "msg": UnicodeParameter(),
        },
    )
    def api_comment_post(self, request, ids, msg):
        alarms = list(ActiveAlarm.objects.filter(id__in=ids))
        alarms += list(ArchivedAlarm.objects.filter(id__in=ids))
        if not alarms:
            self.response_not_found()
        for alarm in alarms:
            alarm.log_message(msg, source=request.user.username)
        return True

    @view(
        url=r"^(?P<id>[a-z0-9]{24})/acknowledge/",
        method=["POST"],
        api=True,
        access="acknowledge",
        validate={
            "msg": UnicodeParameter(default=""),
        },
    )
    def api_acknowledge(self, request, id, msg=""):
        alarm = get_alarm(id)
        if not alarm:
            return self.response_not_found()
        if alarm.status != "A":
            return self.response_not_found()
        if alarm.ack_ts:
            return {
                "status": False,
                "message": "Already acknowledged by %s" % alarm.ack_user
            }
        alarm.acknowledge(request.user, msg)
        return {"status": True}

    @view(
        url=r"^(?P<id>[a-z0-9]{24})/unacknowledge/",
        method=["POST"],
        api=True,
        access="acknowledge",
        validate={
            "msg": UnicodeParameter(default=""),
        },
    )
    def api_unacknowledge(self, request, id, msg=""):
        alarm = get_alarm(id)
        if not alarm:
            return self.response_not_found()
        if alarm.status != "A":
            return self.response_not_found()
        if not alarm.ack_ts:
            return {"status": False, "message": "Already unacknowledged"}
        alarm.unacknowledge(request.user, msg=msg)
        return {"status": 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",
        validate={
            "msg": UnicodeParameter(default=""),
        },
    )
    def api_clear(self, request, id, msg=""):
        alarm = get_alarm(id)
        if not alarm.alarm_class.user_clearable:
            return {"status": False, "error": "Deny clear alarm by user"}
        if alarm.status == "A":
            alarm.clear_alarm("Cleared by %s: %s" % (request.user, msg),
                              source=request.user.username)
        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=r"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 d.items():
                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
                    order = getattr(pv, "display_order", 100)
                    v += [(
                        (order, -c),
                        '<i class="%s" title="%s"></i>%s' %
                        (pv.glyph, pv.name, badge),
                    )]
            return "<span class='x-summary'>%s</span>" % "".join(
                i[1] for i in sorted(v, key=operator.itemgetter(0)))

        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(smart_text(res).strip().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]
        return ["", ""]

    @classmethod
    def f_summary(cls, s):
        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):
                    v += [{
                        "profile": str(pv.id),
                        "glyph": pv.glyph,
                        "display_order": pv.display_order,
                        "profile__label": pv.name,
                        "summary": c,
                    }]
            return 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 r

    def bulk_field_isinmaintenance(self, data):
        if not data:
            return data
        if data[0]["status"] == "A":
            mtc = set(Maintenance.currently_affected())
            for x in data:
                x["isInMaintenance"] = x["managed_object"] in mtc
        else:
            mos = set([x["managed_object"] for x in data])
            mtc = {}
            for mo in list(mos):
                interval = []
                for ao in AffectedObjects._get_collection().find(
                    {"affected_objects.object": {
                        "$eq": mo
                    }}, {
                        "_id": 0,
                        "maintenance": 1
                    }):
                    m = Maintenance.get_by_id(ao["maintenance"])
                    interval += [(m.start, m.stop)]
                if interval:
                    mtc[mo] = interval
            for x in data:
                if x["managed_object"] in mtc:
                    left, right = list(zip(*mtc[x["managed_object"]]))
                    x["isInMaintenance"] = bisect.bisect(
                        right,
                        dateutil.parser.parse(x["timestamp"]).replace(
                            tzinfo=None)) != bisect.bisect(
                                left,
                                dateutil.parser.parse(
                                    x["clear_timestamp"]).replace(tzinfo=None))
                else:
                    x["isInMaintenance"] = False
        return data

    @view(url=r"profile_lookup/$", access="launch", method=["GET"], api=True)
    def api_profile_lookup(self, request):
        r = []
        for model, short_type, field_id in (
            (ServiceProfile, _("Service"), "total_services"),
            (SubscriberProfile, _("Subscribers"), "total_subscribers"),
        ):
            # "%s|%s" % (field_id,
            r += [{
                "id": str(o.id),
                "type": short_type,
                "display_order": o.display_order,
                "icon": o.glyph,
                "label": o.name,
            } for o in model.objects.all()
                  if getattr(o, "show_in_summary", True)]
        return r
Beispiel #14
0
def test_unicode_parameter_error(raw, config):
    with pytest.raises(InterfaceTypeError):
        assert UnicodeParameter(**config).clean(raw)
Beispiel #15
0
def test_unicode_parameter(raw, config, expected):
    assert UnicodeParameter(**config).clean(raw) == expected
Beispiel #16
0
class InvApplication(ExtApplication):
    """
    inv.inv application
    """

    title = _("Inventory")
    menu = _("Inventory")

    # Undeletable nodes
    UNDELETABLE = {
        # 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("services/web/apps/inv/inv/plugins/"):
            if not f.endswith(".py") or f == "base.py" or f.startswith("_"):
                continue
            mn = "noc.services.web.apps.inv.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_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):
        children = []
        if request.GET and "node" in request.GET:
            container = request.GET["node"]
            if is_objectid(container):
                container = Object.get_by_id(container)
                if not container:
                    return self.response_not_found()
                children = [(o.name, o) for o in Object.objects.filter(container=container.id)]
                # Collect inner connections
                children += [(name, o) for name, o, _ in container.get_inner_connections()]
            elif container == "root":
                cmodels = [
                    d["_id"]
                    for d in ObjectModel._get_collection().find(
                        {"data.container.container": True}, {"_id": 1}
                    )
                ]
                children = [
                    (o.name, o)
                    for o in Object.objects.filter(
                        __raw__={"container": None, "model": {"$in": cmodels}}
                    )
                ]

            else:
                return self.response_bad_request()
        r = []
        # Build node interface
        for name, o in children:
            m_plugins = o.model.plugins or []
            disabled_plugins = set(p[1:] for p in m_plugins if p.startswith("-"))
            n = {
                "id": str(o.id),
                "name": name,
                "plugins": [],
                "can_add": bool(o.get_data("container", "container")),
                "can_delete": str(o.model.uuid) not in self.UNDELETABLE,
            }
            if o.get_data("container", "container") or o.has_inner_connections():
                # n["expanded"] = Object.objects.filter(container=o.id).count() == 1
                n["expanded"] = False
            else:
                n["leaf"] = True
            if o.get_data("rack", "units"):
                n["plugins"] += [self.get_plugin_data("rack")]
            if o.model.connections:
                n["plugins"] += [self.get_plugin_data("inventory")]
            if o.get_data("geopoint", "layer"):
                n["plugins"] += [self.get_plugin_data("map")]
            if o.get_data("management", "managed_object"):
                n["plugins"] += [self.get_plugin_data("managedobject")]
            if o.get_data("contacts", "has_contacts"):
                n["plugins"] += [self.get_plugin_data("contacts")]
            # Append model's plugins
            for p in m_plugins:
                if not p.startswith("-"):
                    n["plugins"] += [self.get_plugin_data(p)]
            n["plugins"] += [
                self.get_plugin_data("data"),
                self.get_plugin_data("comment"),
                self.get_plugin_data("file"),
                self.get_plugin_data("log"),
            ]
            # Process disabled plugins
            n["plugins"] = [p for p in n["plugins"] if p["name"] not in disabled_plugins]
            r += [n]
        return r

    @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 is_objectid(container):
            c = Object.get_by_id(container)
            if not c:
                return self.response_not_found()
            c = c.id
        elif container:
            return self.response_bad_request()
        else:
            c = None
        m = ObjectModel.get_by_id(type)
        if not m:
            return self.response_not_found()
        o = Object(name=name, model=m, container=c)
        if serial and m.get_data("asset", "part_no0"):
            o.set_data("asset", "serial", serial)
        o.save()
        o.log("Created", user=request.user.username, system="WEB", op="CREATE")
        return str(o.id)

    @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):
        """
        :param request:
        :param container: ObjectID after/in that insert
        :param objects: List ObjectID for insert
        :param position: 'append', 'before', 'after'
        :return:
        """
        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.id) if c.container else None
            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}]
        while o.container:
            o = o.container
            path.insert(0, {"id": str(o.id), "name": o.name})
        return path
Beispiel #17
0
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 "&nbsp;".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 ["", ""]
Beispiel #18
0
class PrefixListBuilderApplication(ExtApplication):
    """
    Interactive prefix list builder
    """
    title = _("Prefix List Builder")
    menu = _("Prefix List Builder")

    @view(method=["GET"],
          url=r"^$",
          access="read",
          api=True,
          validate={
              "peering_point": ModelParameter(PeeringPoint),
              "name": UnicodeParameter(required=False),
              "as_set": UnicodeParameter()
          })
    def api_list(self, request, peering_point, name, as_set):
        if not WhoisCache.has_asset_members():
            return {
                "name":
                name,
                "prefix_list":
                "",
                "success":
                False,
                "message":
                _("AS-SET members cache is empty. Please update Whois Cache")
            }
        if not WhoisCache.has_origin_routes():
            return {
                "name":
                name,
                "prefix_list":
                "",
                "success":
                False,
                "message":
                _("Origin routes cache is empty. Please update Whois Cache")
            }
        if not WhoisCache.has_asset(as_set):
            return {
                "name": name,
                "prefix_list": "",
                "success": False,
                "message": _("Unknown AS-SET")
            }
        prefixes = WhoisCache.resolve_as_set_prefixes_maxlen(as_set)
        if not prefixes:
            return {
                "name": name,
                "prefix_list": "",
                "success": False,
                "message": _("Cannot resolve AS-SET prefixes")
            }
        try:
            pl = peering_point.profile.get_profile().generate_prefix_list(
                name, prefixes)
        except NotImplementedError:
            return {
                "name":
                name,
                "prefix_list":
                "",
                "success":
                False,
                "message":
                _("Prefix-list generator is not implemented for this profile")
            }
        return {
            "name": name,
            "prefix_list": pl,
            "success": True,
            "message": _("Prefix List built")
        }
Beispiel #19
0
class InvApplication(ExtApplication):
    """
    inv.inv application
    """

    title = _("Inventory")
    menu = _("Inventory")

    # Undeletable nodes
    UNDELETABLE = {
        # 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("services/web/apps/inv/inv/plugins/"):
            if not f.endswith(".py") or f == "base.py" or f.startswith("_"):
                continue
            mn = "noc.services.web.apps.inv.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_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):
        children = []
        if request.GET and "node" in request.GET:
            container = request.GET["node"]
            if is_objectid(container):
                container = Object.get_by_id(container)
                if not container:
                    return self.response_not_found()
                children = [
                    (o.name, o)
                    for o in Object.objects.filter(container=container.id)
                ]
                # Collect inner connections
                children += [
                    (name, o)
                    for name, o, _ in container.get_inner_connections()
                ]
            elif container == "root":
                cmodels = [
                    d["_id"] for d in ObjectModel._get_collection().find(
                        {"data.container.container": True}, {"_id": 1})
                ]
                children = [(o.name, o)
                            for o in Object.objects.filter(__raw__={
                                "container": None,
                                "model": {
                                    "$in": cmodels
                                }
                            })]

            else:
                return self.response_bad_request()
        r = []
        # Build node interface
        for name, o in children:
            m_plugins = o.model.plugins or []
            disabled_plugins = set(p[1:] for p in m_plugins
                                   if p.startswith("-"))
            n = {
                "id": str(o.id),
                "name": name,
                "plugins": [],
                "can_add": bool(o.get_data("container", "container")),
                "can_delete": str(o.model.uuid) not in self.UNDELETABLE,
            }
            if o.get_data("container",
                          "container") or o.has_inner_connections():
                # n["expanded"] = Object.objects.filter(container=o.id).count() == 1
                n["expanded"] = False
            else:
                n["leaf"] = True
            if o.get_data("rack", "units"):
                n["plugins"] += [self.get_plugin_data("rack")]
            if o.model.connections:
                n["plugins"] += [self.get_plugin_data("inventory")]
            if o.get_data("geopoint", "layer"):
                n["plugins"] += [self.get_plugin_data("map")]
            if o.get_data("management", "managed_object"):
                n["plugins"] += [self.get_plugin_data("managedobject")]
            if o.get_data("contacts", "has_contacts"):
                n["plugins"] += [self.get_plugin_data("contacts")]
            # Append model's plugins
            for p in m_plugins:
                if not p.startswith("-"):
                    n["plugins"] += [self.get_plugin_data(p)]
            n["plugins"] += [
                self.get_plugin_data("data"),
                self.get_plugin_data("comment"),
                self.get_plugin_data("file"),
                self.get_plugin_data("log"),
            ]
            # Process disabled plugins
            n["plugins"] = [
                p for p in n["plugins"] if p["name"] not in disabled_plugins
            ]
            r += [n]
        return r

    @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 is_objectid(container):
            c = Object.get_by_id(container)
            if not c:
                return self.response_not_found()
            c = c.id
        elif container:
            return self.response_bad_request()
        else:
            c = None
        m = ObjectModel.get_by_id(type)
        if not m:
            return self.response_not_found()
        o = Object(name=name, model=m, container=c)
        if serial and m.get_data("asset", "part_no0"):
            o.set_data("asset", "serial", serial)
        o.save()
        o.log("Created", user=request.user.username, system="WEB", op="CREATE")
        return str(o.id)

    @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):
        """
        :param request:
        :param container: ObjectID after/in that insert
        :param objects: List ObjectID for insert
        :param position: 'append', 'before', 'after'
        :return:
        """
        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.id) if c.container else None
            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}]
        while o.container:
            o = o.container
            path.insert(0, {"id": str(o.id), "name": o.name})
        return path

    @view(
        "^crossing_proposals/$",
        method=["GET"],
        access="read",
        api=True,
        validate={
            "o1": ObjectIdParameter(required=True),
            "o2": ObjectIdParameter(required=False),
            "left_filter": UnicodeParameter(required=False),
            "right_filter": UnicodeParameter(required=False),
            "cable_filter": UnicodeParameter(required=False),
        },
    )
    def api_get_crossing_proposals(
        self,
        request,
        o1,
        o2=None,
        left_filter: Optional[str] = None,
        right_filter: Optional[str] = None,
        cable_filter: Optional[str] = None,
    ):
        """
        API for connnection form.
        1) If cable_filter set, checked connection capable with cable.
        2) If left_filter set, check renmote object
        :param request:
        :param o1:
        :param o2:
        :param left_filter:
        :param right_filter:
        :param cable_filter:
        :return:
        """
        self.logger.info(
            "Crossing proposals: %s:%s, %s:%s. Cable: %s",
            o1,
            left_filter,
            o2,
            right_filter,
            cable_filter,
        )
        lo: Object = self.get_object_or_404(Object, id=o1)
        ro: Optional[Object] = None
        if o2:
            ro = self.get_object_or_404(Object, id=o2)
        lcs: List[Dict[str, Any]] = []
        cable: Optional[ObjectModel] = None
        # Getting cable
        cables = ObjectModel.objects.filter(data__length__length__gte=0)
        if cable_filter:
            cable = ObjectModel.get_by_name(cable_filter)
        for c in lo.model.connections:
            valid, disable_reason = True, ""
            if cable_filter:
                # If select cable_filter - check every connection to cable
                cable_connections = [
                    c for c in lo.model.get_connection_proposals(c.name)
                    if c[0] == cable.id
                ]
                valid = bool(cable_connections)
            elif ro and right_filter:
                rc = ro.model.get_model_connection(right_filter)
                if not rc:
                    raise
                valid, disable_reason = lo.model.check_connection(c, rc)
            elif ro:
                valid = bool([
                    c for c in lo.model.get_connection_proposals(c.name)
                    if c[0] == ro.model.id
                ])
            oc, oo, _ = lo.get_p2p_connection(c.name)
            lcs += [{
                "name": c.name,
                "type": str(c.type.id),
                "type__label": c.type.name,
                "gender": c.gender,
                "direction": c.direction,
                "protocols": c.protocols,
                "free": not bool(oc),
                "valid": valid,
                "disable_reason": disable_reason,
            }]
        rcs: List[Dict[str, Any]] = []
        if ro:
            for c in ro.model.connections:
                valid, disable_reason = True, ""
                if cable_filter:
                    cable_connections = [
                        c for c in ro.model.get_connection_proposals(c.name)
                        if c[0] == cable.id
                    ]
                    valid = bool(cable_connections)
                elif left_filter:
                    lc = lo.model.get_model_connection(left_filter)
                    if not lc:
                        raise
                    valid, disable_reason = lo.model.check_connection(c, lc)
                else:
                    valid = bool([
                        c for c in ro.model.get_connection_proposals(c.name)
                        if c[0] == lo.model.id
                    ])
                oc, oo, _ = ro.get_p2p_connection(c.name)
                rcs += [{
                    "name": c.name,
                    "type": str(c.type.id),
                    "type__label": c.type.name,
                    "gender": c.gender,
                    "direction": c.direction,
                    "protocols": c.protocols,
                    "free": not bool(oc),
                    "valid": valid,
                    "disable_reason": disable_reason,
                }]
        # Forming cable
        return {
            "left": {
                "connections": lcs
            },
            "right": {
                "connections": rcs
            },
            "cable": [{
                "name": c.name,
                "available": True
            } for c in cables],
            "valid": lcs and rcs and left_filter and right_filter,
        }

    @view(
        "^connect/$",
        method=["POST"],
        access="connect",
        api=True,
        validate={
            "object": ObjectIdParameter(required=True),
            "name": StringParameter(required=True),
            "remote_object": ObjectIdParameter(required=True),
            "remote_name": StringParameter(required=True),
            # "cable": ObjectIdParameter(required=False),
            "cable": StringParameter(required=False),
            "reconnect": BooleanParameter(default=False, required=False),
        },
    )
    def api_connect(
        self,
        request,
        object,
        name,
        remote_object,
        remote_name,
        cable: Optional[str] = None,
        reconnect=False,
    ):
        lo: Object = self.get_object_or_404(Object, id=object)
        ro: Object = self.get_object_or_404(Object, id=remote_object)
        cable_o: Optional[Object] = None
        if cable:
            cable = ObjectModel.get_by_name(cable)
            cable_o = Object(
                name="Wire %s:%s <-> %s:%s" %
                (lo.name, name, ro.name, remote_name),
                model=cable,
                container=lo.container.id,
            )
            cable_o.save()
        print(lo, ro, cable_o)
        try:
            if cable_o:
                c1, c2 = cable_o.model.connections[:2]
                self.logger.debug("Wired connect c1:c2", c1, c2)
                lo.connect_p2p(name, cable_o, c1.name, {}, reconnect=reconnect)
                ro.connect_p2p(remote_name,
                               cable_o,
                               c2.name, {},
                               reconnect=reconnect)
                lo.save()
                ro.save()
            else:
                lo.connect_p2p(name, ro, remote_name, {}, reconnect=reconnect)
        except ConnectionError as e:
            self.logger.warning("Connection Error: %s", str(e))
            return self.render_json({"status": False, "text": str(e)})
        return True