Esempio n. 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"
Esempio n. 2
0
 def api_action_group_edit(self, request):
     validator = DictParameter(
         attrs={"ids": ListOfParameter(element=ModelParameter(self.model), convert=True)}
     )
     rv = self.deserialize(request.body)
     try:
         v = validator.clean(rv)
     except InterfaceTypeError as e:
         self.logger.info("Bad request: %r (%s)", request.body, e)
         return self.render_json(
             {"status": False, "message": "Bad request", "traceback": str(e)},
             status=self.BAD_REQUEST,
         )
     objects = v["ids"]
     del v["ids"]
     try:
         v = self.clean(v)
     except ValueError as e:
         return self.render_json(
             {"status": False, "message": "Bad request", "traceback": str(e)},
             status=self.BAD_REQUEST,
         )
     for o in objects:
         for p in v:
             setattr(o, p, v[p])
         o.save()
     return "%d records has been updated" % len(objects)
Esempio n. 3
0
File: views.py Progetto: nbashev/noc
class UnknownModelApplication(ExtDocApplication):
    """
    UnknownModel application
    """

    title = _("Unknown Models")
    menu = _("Unknown Models")
    model = UnknownModel

    query_condition = "icontains"
    query_fields = [
        "vendor", "managed_object", "platform", "part_no", "description"
    ]

    @view(
        url="^actions/remove/$",
        method=["POST"],
        access="launch",
        api=True,
        validate={
            "ids":
            ListOfParameter(element=DocumentParameter(UnknownModel),
                            convert=True)
        },
    )
    def api_action_run_discovery(self, request, ids):
        UnknownModel.objects.filter(id__in=[x.id for x in ids])
        return _("Cleaned")
Esempio n. 4
0
class PrefixTableApplication(ExtModelApplication):
    """
    PrefixTable application
    """

    title = _("Prefix Table")
    menu = [_("Setup"), _("Prefix Tables")]
    model = PrefixTable

    prefixes = ModelInline(PrefixTablePrefix)

    @view(
        url="^actions/test/$",
        method=["POST"],
        access="update",
        api=True,
        validate={
            "ids": ListOfParameter(element=ModelParameter(PrefixTable)),
            "ip": IPParameter()
        },
    )
    def api_action_test(self, request, ids, ip):
        return {
            "ip":
            ip,
            "result": [{
                "id": pt.id,
                "name": pt.name,
                "result": ip in pt
            } for pt in ids],
        }
Esempio n. 5
0
File: views.py Progetto: nbashev/noc
class TimePatternApplication(ExtModelApplication):
    """
    TimePattern application
    """

    title = _("Time Pattern")
    menu = [_("Setup"), _("Time Patterns")]
    model = TimePattern
    glyph = "clock-o"

    terms = ModelInline(TimePatternTerm)

    @view(
        url="^actions/test/",
        method=["POST"],
        access="read",
        api=True,
        validate={
            "ids": ListOfParameter(element=ModelParameter(TimePattern)),
            "date": StringParameter(required=True),
            "time": StringParameter(required=True),
        },
    )
    def api_action_test(self, request, ids, date=None, time=None):
        d = "%sT%s" % (date, time)
        dt = datetime.datetime.strptime(d, "%Y-%m-%dT%H:%M")
        return {
            "ts":
            dt.isoformat(),
            "result": [{
                "id": p.id,
                "name": p.name,
                "result": p.match(dt)
            } for p in ids],
        }
Esempio n. 6
0
class ConnectionRuleApplication(ExtDocApplication):
    """
    ConnectionRule application
    """

    title = _("Connection Rules")
    menu = [_("Setup"), _("Connection Rules")]
    model = ConnectionRule
    query_fields = ["name__icontains", "description__icontains"]

    @view(
        url="^actions/json/$",
        method=["POST"],
        access="read",
        validate={
            "ids":
            ListOfParameter(element=DocumentParameter(ConnectionRule),
                            convert=True)
        },
        api=True,
    )
    def api_action_json(self, request, ids):
        r = [o.json_data for o in ids]
        s = to_json(r, order=["name", "description"])
        return {"data": s}
Esempio n. 7
0
File: views.py Progetto: nbashev/noc
class VRFApplication(ExtModelApplication):
    """
    VRF application
    """

    title = _("VRF")
    menu = _("VRF")
    model = VRF
    query_fields = ["name", "rd", "description"]

    mrt_config = {
        "get_vrfs": {
            "map_script": "get_mpls_vpn",
            "access": "import"
        }
    }

    def field_row_class(self, o):
        return o.profile.style.css_class_name if o.profile.style else ""

    def clean(self, data):
        if not data.get("vpn_id"):
            vdata = {"type": "VRF", "name": data["name"], "rd": data.get("rd")}
            data["vpn_id"] = get_vpn_id(vdata)
        return super().clean(data)

    @view(
        url="^bulk/import/$",
        method=["POST"],
        access="import",
        api=True,
        validate={
            "items":
            ListOfParameter(element=DictParameter(
                attrs={
                    "name": StringParameter(),
                    "rd": RDParameter(),
                    "vrf_group": ModelParameter(model=VRFGroup),
                    "afi_ipv4": BooleanParameter(default=False),
                    "afi_ipv6": BooleanParameter(default=False),
                    "description": StringParameter(required=False),
                }))
        },
    )
    def api_bulk_import(self, request, items):
        n = 0
        for i in items:
            if not VRF.objects.filter(name=i["name"], rd=i["rd"]).exists():
                # Add only new
                VRF(
                    name=i["name"],
                    vrf_group=i["vrf_group"],
                    rd=i["rd"],
                    afi_ipv4=i["afi_ipv4"],
                    afi_ipv6=i["afi_ipv6"],
                    description=i.get("description"),
                ).save()
                n += 1
        return {"status": True, "imported": n}
Esempio n. 8
0
def test_listof_parameter():
    assert ListOfParameter(element=IntParameter()).clean([1, 2,
                                                          3]) == [1, 2, 3]
    assert ListOfParameter(element=IntParameter()).clean([1, 2,
                                                          "3"]) == [1, 2, 3]
    with pytest.raises(InterfaceTypeError):
        ListOfParameter(element=IntParameter()).clean([1, 2, "x"])
    assert ListOfParameter(element=StringParameter()).clean(
        [1, 2, 3, "x"]) == ["1", "2", "3", "x"]
    assert ListOfParameter(element=StringParameter(),
                           default=[]).clean(None) == []
    assert ListOfParameter(element=StringParameter(),
                           default=[1, 2, 3]).clean(None) == ["1", "2", "3"]
    assert ListOfParameter(
        element=[StringParameter(), IntParameter()]).clean([
            ("a", 1), ("b", "2")
        ]) == [["a", 1], ["b", 2]]
    with pytest.raises(InterfaceTypeError):
        assert ListOfParameter(
            element=[StringParameter(), IntParameter()]).clean([("a", 1),
                                                                ("b", "x")])
Esempio n. 9
0
class ASSetApplication(ExtModelApplication):
    """
    ASSet application
    """
    title = "AS Sets"
    menu = "AS Sets"
    model = ASSet
    query_fields = ["name__icontains","description__icontains",
                    "members__icontains"]


    @view(url="^actions/rpsl/$", method=["POST"],
        access="read", api=True,
        validate={
            "ids": ListOfParameter(element=ModelParameter(ASSet))
        })

    def api_action_rpsl(self,request,ids):
        return "</br></br>".join([o.rpsl.replace("\n", "</br>") for o in ids])
    api_action_rpsl.short_description="RPSL for selected objects"
Esempio n. 10
0
def test_listof_parameter(raw, config, expected):
    assert ListOfParameter(**config).clean(raw) == expected
Esempio n. 11
0
class ManagedObjectApplication(ExtModelApplication):
    """
    ManagedObject application
    """

    title = _("Managed Objects")
    menu = _("Managed Objects")
    model = ManagedObject
    query_condition = "icontains"
    query_fields = ["name", "description"]
    secret_fields = {"password", "super_password", "snmp_ro", "snmp_rw"}
    # Inlines
    attrs = ModelInline(ManagedObjectAttribute)
    cfg = RepoInline("config", access="config")

    extra_permissions = ["alarm", "change_interface"]
    implied_permissions = {
        "read": ["inv:networksegment:lookup", "main:handler:lookup"]
    }
    diverged_permissions = {"config": "read", "console": "script"}
    order_map = {
        "address":
        " cast_test_to_inet(address) ",
        "-address":
        " cast_test_to_inet(address) ",
        "profile":
        "CASE %s END" % " ".join([
            "WHEN %s='%s' THEN %s" % ("profile", pk, i) for i, pk in enumerate(
                Profile.objects.filter().order_by("name").values_list("id"))
        ]),
        "-profile":
        "CASE %s END" % " ".join([
            "WHEN %s='%s' THEN %s" % ("profile", pk, i) for i, pk in enumerate(
                Profile.objects.filter().order_by("-name").values_list("id"))
        ]),
        "platform":
        "CASE %s END" % " ".join([
            "WHEN %s='%s' THEN %s" % ("platform", pk, i)
            for i, pk in enumerate(Platform.objects.filter().order_by(
                "name").values_list("id"))
        ]),
        "-platform":
        "CASE %s END" % " ".join([
            "WHEN %s='%s' THEN %s" % ("platform", pk, i)
            for i, pk in enumerate(Platform.objects.filter().order_by(
                "-name").values_list("id"))
        ]),
        "version":
        "CASE %s END" % " ".join([
            "WHEN %s='%s' THEN %s" % ("version", pk, i)
            for i, pk in enumerate(Firmware.objects.filter().order_by(
                "version").values_list("id"))
        ]),
        "-version":
        "CASE %s END" % " ".join([
            "WHEN %s='%s' THEN %s" % ("version", pk, i)
            for i, pk in enumerate(Firmware.objects.filter().order_by(
                "-version").values_list("id"))
        ]),
    }
    resource_group_fields = [
        "static_service_groups",
        "effective_service_groups",
        "static_client_groups",
        "effective_client_groups",
    ]

    DISCOVERY_JOBS = [
        ("box", "noc.services.discovery.jobs.box.job.BoxDiscoveryJob"),
        ("periodic",
         "noc.services.discovery.jobs.periodic.job.PeriodicDiscoveryJob"),
    ]

    def field_row_class(self, o):
        return o.object_profile.style.css_class_name if o.object_profile.style else ""

    def bulk_field_interface_count(self, data):
        """
        Apply interface_count fields
        :param data:
        :return:
        """
        mo_ids = [x["id"] for x in data]
        if not mo_ids:
            return data
        # Collect interface counts
        r = Interface._get_collection().aggregate([
            {
                "$match": {
                    "managed_object": {
                        "$in": mo_ids
                    },
                    "type": "physical"
                }
            },
            {
                "$group": {
                    "_id": "$managed_object",
                    "total": {
                        "$sum": 1
                    }
                }
            },
        ])
        ifcount = dict((x["_id"], x["total"]) for x in r)
        # Apply interface counts
        for x in data:
            x["interface_count"] = ifcount.get(x["id"]) or 0
        return data

    def bulk_field_link_count(self, data):
        """
        Apply link_count fields
        :param data:
        :return:
        """
        mo_ids = [x["id"] for x in data]
        if not mo_ids:
            return data
        # Collect interface counts
        r = Link._get_collection().aggregate([
            {
                "$match": {
                    "linked_objects": {
                        "$in": mo_ids
                    }
                }
            },
            {
                "$unwind": "$linked_objects"
            },
            {
                "$group": {
                    "_id": "$linked_objects",
                    "total": {
                        "$sum": 1
                    }
                }
            },
        ])
        links_count = dict((x["_id"], x["total"]) for x in r)
        # Apply interface counts
        for x in data:
            x["link_count"] = links_count.get(x["id"]) or 0
        return data

    def instance_to_dict(self, o, fields=None):
        def sg_to_list(items):
            return [{
                "group": x,
                "group__label": unicode(ResourceGroup.get_by_id(x))
            } for x in items]

        data = super(ManagedObjectApplication,
                     self).instance_to_dict(o, fields)
        # Expand resource groups fields
        for fn in self.resource_group_fields:
            data[fn] = sg_to_list(data.get(fn) or [])
        return data

    def clean(self, data):
        # Clean resource groups
        for fn in self.resource_group_fields:
            if fn.startswith("effective_") and fn in data:
                del data[fn]
                continue
            data[fn] = [x["group"] for x in (data.get(fn) or [])]
        # Clean other
        return super(ManagedObjectApplication, self).clean(data)

    def cleaned_query(self, q):
        if "administrative_domain" in q:
            ad = AdministrativeDomain.get_nested_ids(
                int(q["administrative_domain"]))
            if ad:
                del q["administrative_domain"]
        else:
            ad = None
        if "selector" in q:
            s = self.get_object_or_404(ManagedObjectSelector,
                                       id=int(q["selector"]))
            del q["selector"]
        else:
            s = None
        r = super(ManagedObjectApplication, self).cleaned_query(q)
        if s:
            r["id__in"] = ManagedObject.objects.filter(s.Q)
        if ad:
            r["administrative_domain__in"] = ad
        return r

    def get_Q(self, request, query):
        q = super(ManagedObjectApplication, self).get_Q(request, query)
        sq = ManagedObject.get_search_Q(query)
        if sq:
            q |= sq
        return q

    def queryset(self, request, query=None):
        qs = super(ManagedObjectApplication, self).queryset(request, query)
        if not request.user.is_superuser:
            qs = qs.filter(UserAccess.Q(request.user))
        qs = qs.exclude(name__startswith="wiping-")
        return qs

    @view(url=r"^(?P<id>\d+)/links/$", method=["GET"], access="read", api=True)
    def api_links(self, request, id):
        o = self.get_object_or_404(ManagedObject, id=id)
        if not o.has_access(request.user):
            return self.response_forbidden("Access denied")
        # Get links
        result = []
        for link in Link.object_links(o):
            ifaces = []
            r = []
            for i in link.interfaces:
                if i.managed_object.id == o.id:
                    ifaces += [i]
                else:
                    r += [i]
            for li, ri in zip(ifaces, r):
                result += [{
                    "link_id":
                    str(link.id),
                    "local_interface":
                    str(li.id),
                    "local_interface__label":
                    li.name,
                    "remote_object":
                    ri.managed_object.id,
                    "remote_object__label":
                    ri.managed_object.name,
                    "remote_platform":
                    ri.managed_object.platform.name
                    if ri.managed_object.platform else "",
                    "remote_interface":
                    str(ri.id),
                    "remote_interface__label":
                    ri.name,
                    "discovery_method":
                    link.discovery_method,
                    "local_description":
                    li.description,
                    "remote_description":
                    ri.description,
                    "first_discovered":
                    link.first_discovered.isoformat()
                    if link.first_discovered else None,
                    "last_seen":
                    link.last_seen.isoformat() if link.last_seen else None,
                }]
        return result

    @view(url=r"^(?P<id>\d+)/discovery/$",
          method=["GET"],
          access="read",
          api=True)
    def api_discovery(self, request, id):
        from noc.core.scheduler.job import Job

        o = self.get_object_or_404(ManagedObject, id=id)
        if not o.has_access(request.user):
            return self.response_forbidden("Access denied")
        link_count = defaultdict(int)
        for link in Link.object_links(o):
            m = link.discovery_method or ""
            if "+" in m:
                m = m.split("+")[0]
            link_count[m] += 1
        r = [{
            "name": "ping",
            "enable_profile": o.object_profile.enable_ping,
            "status": o.get_status(),
            "last_run": None,
            "last_status": None,
            "next_run": None,
            "jcls": None,
        }]

        for name, jcls in self.DISCOVERY_JOBS:
            job = Job.get_job_data(
                "discovery", jcls=jcls, key=o.id, pool=o.pool.name) or {}
            d = {
                "name":
                name,
                "enable_profile":
                getattr(o.object_profile, "enable_%s_discovery" % name),
                "status":
                job.get(Job.ATTR_STATUS),
                "last_run":
                self.to_json(job.get(Job.ATTR_LAST)),
                "last_status":
                job.get(Job.ATTR_LAST_STATUS),
                "next_run":
                self.to_json(job.get(Job.ATTR_TS)),
                "jcls":
                jcls,
            }
            r += [d]
        return r

    @view(
        url=r"^actions/set_managed/$",
        method=["POST"],
        access="create",
        api=True,
        validate={
            "ids":
            ListOfParameter(element=ModelParameter(ManagedObject),
                            convert=True)
        },
    )
    def api_action_set_managed(self, request, ids):
        for o in ids:
            if not o.has_access(request.user):
                continue
            o.is_managed = True
            o.save()
        return "Selected objects set to managed state"

    @view(
        url=r"^actions/set_unmanaged/$",
        method=["POST"],
        access="create",
        api=True,
        validate={
            "ids":
            ListOfParameter(element=ModelParameter(ManagedObject),
                            convert=True)
        },
    )
    def api_action_set_unmanaged(self, request, ids):
        for o in ids:
            if not o.has_access(request.user):
                continue
            o.is_managed = False
            o.save()
        return "Selected objects set to unmanaged state"

    @view(url=r"^(?P<id>\d+)/discovery/run/$",
          method=["POST"],
          access="change_discovery",
          api=True)
    def api_run_discovery(self, request, id):
        o = self.get_object_or_404(ManagedObject, id=id)
        if not o.has_access(request.user):
            return self.response_forbidden("Access denied")
        r = ujson.loads(request.body).get("names", [])
        for name, jcls in self.DISCOVERY_JOBS:
            if name not in r:
                continue
            if not getattr(o.object_profile, "enable_%s_discovery" % name):
                continue  # Disabled by profile
            Job.submit("discovery", jcls, key=o.id, pool=o.pool.name)
        return {"success": True}

    @view(url=r"^(?P<id>\d+)/discovery/stop/$",
          method=["POST"],
          access="change_discovery",
          api=True)
    def api_stop_discovery(self, request, id):
        o = self.get_object_or_404(ManagedObject, id=id)
        if not o.has_access(request.user):
            return self.response_forbidden("Access denied")
        r = ujson.loads(request.body).get("names", [])
        for name, jcls in self.DISCOVERY_JOBS:
            if name not in r:
                continue
            if not getattr(o.object_profile, "enable_%s_discovery" % name):
                continue  # Disabled by profile
            Job.remove("discovery", jcls, key=o.id, pool=o.pool.name)
        return {"success": True}

    @view(url=r"^(?P<id>\d+)/interface/$",
          method=["GET"],
          access="read",
          api=True)
    def api_interface(self, request, id):
        """
        GET interfaces
        :param managed_object:
        :return:
        """
        def sorted_iname(s):
            return sorted(s, key=lambda x: split_alnum(x["name"]))

        def get_style(i):
            profile = i.profile
            if profile:
                try:
                    return style_cache[profile.id]
                except KeyError:
                    pass
                if profile.style:
                    s = profile.style.css_class_name
                else:
                    s = ""
                style_cache[profile.id] = s
                return s
            else:
                return ""

        def get_link(i):
            link = i.link
            if not link:
                return None
            if link.is_ptp:
                # ptp
                o = link.other_ptp(i)
                label = "%s:%s" % (o.managed_object.name, o.name)
            elif link.is_lag:
                # unresolved LAG
                o = [
                    ii for ii in link.other(i)
                    if ii.managed_object.id != i.managed_object.id
                ]
                label = "LAG %s: %s" % (o[0].managed_object.name, ", ".join(
                    ii.name for ii in o))
            else:
                # Broadcast
                label = ", ".join("%s:%s" % (ii.managed_object.name, ii.name)
                                  for ii in link.other(i))
            return {"id": str(link.id), "label": label}

        # Get object
        o = self.get_object_or_404(ManagedObject, id=int(id))
        if not o.has_access(request.user):
            return self.response_forbidden("Permission denied")
        # Physical interfaces
        # @todo: proper ordering
        default_state = ResourceState.get_default()
        style_cache = {}  # profile_id -> css_style
        l1 = [{
            "id":
            str(i.id),
            "name":
            i.name,
            "description":
            i.description,
            "status":
            i.status,
            "mac":
            i.mac,
            "ifindex":
            i.ifindex,
            "lag":
            (i.aggregated_interface.name if i.aggregated_interface else ""),
            "link":
            get_link(i),
            "profile":
            str(i.profile.id) if i.profile else None,
            "profile__label":
            unicode(i.profile) if i.profile else None,
            "enabled_protocols":
            i.enabled_protocols,
            "project":
            i.project.id if i.project else None,
            "project__label":
            unicode(i.project) if i.project else None,
            "state":
            i.state.id if i.state else default_state.id,
            "state__label":
            unicode(i.state if i.state else default_state),
            "vc_domain":
            i.vc_domain.id if i.vc_domain else None,
            "vc_domain__label":
            unicode(i.vc_domain) if i.vc_domain else None,
            "row_class":
            get_style(i),
        } for i in Interface.objects.filter(managed_object=o.id,
                                            type="physical")]
        # LAG
        lag = [{
            "id":
            str(i.id),
            "name":
            i.name,
            "description":
            i.description,
            "profile":
            str(i.profile.id) if i.profile else None,
            "profile__label":
            unicode(i.profile) if i.profile else None,
            "members": [
                j.name
                for j in Interface.objects.filter(managed_object=o.id,
                                                  aggregated_interface=i.id)
            ],
            "row_class":
            get_style(i),
        } for i in Interface.objects.filter(managed_object=o.id,
                                            type="aggregated")]
        # L2 interfaces
        l2 = [{
            "name": i.name,
            "description": i.description,
            "untagged_vlan": i.untagged_vlan,
            "tagged_vlans": i.tagged_vlans,
        } for i in SubInterface.objects.filter(managed_object=o.id,
                                               enabled_afi="BRIDGE")]
        # L3 interfaces
        q = MQ(enabled_afi="IPv4") | MQ(enabled_afi="IPv6")
        l3 = [{
            "name": i.name,
            "description": i.description,
            "ipv4_addresses": i.ipv4_addresses,
            "ipv6_addresses": i.ipv6_addresses,
            "enabled_protocols": i.enabled_protocols,
            "vlan": i.vlan_ids,
            "vrf": i.forwarding_instance.name if i.forwarding_instance else "",
            "mac": i.mac,
        } for i in SubInterface.objects.filter(managed_object=o.id).filter(q)]
        return {
            "l1": sorted_iname(l1),
            "lag": sorted_iname(lag),
            "l2": sorted_iname(l2),
            "l3": sorted_iname(l3),
        }

    @view(url=r"^(?P<id>\d+)/interface/$",
          method=["POST"],
          access="change_interface",
          api=True)
    def api_set_interface(self, request, id):
        def get_or_none(c, v):
            if not v:
                return None
            return c.objects.get(id=v)

        o = self.get_object_or_404(ManagedObject, id=int(id))
        if not o.has_access(request.user):
            return self.response_forbidden("Access denied")
        d = ujson.loads(request.body)
        if "id" in d:
            i = self.get_object_or_404(Interface, id=d["id"])
            if i.managed_object.id != o.id:
                return self.response_not_found()
            # Set profile
            if "profile" in d:
                p = get_or_none(InterfaceProfile, d["profile"])
                i.profile = p
                if p:
                    i.profile_locked = True
            # Project
            if "project" in d:
                i.project = get_or_none(Project, d["project"])
            # State
            if "state" in d:
                i.state = get_or_none(ResourceState, d["state"])
            # VC Domain
            if "vc_domain" in d:
                i.vc_domain = get_or_none(VCDomain, d["vc_domain"])
            #
            i.save()
        return {"success": True}

    @view(method=["DELETE"], url=r"^(?P<id>\d+)/?$", access="delete", api=True)
    def api_delete(self, request, id):
        """
        Override default method
        :param request:
        :param id:
        :return:
        """
        try:
            o = self.queryset(request).get(id=int(id))
        except self.model.DoesNotExist:
            return self.render_json({
                "status": False,
                "message": "Not found"
            },
                                    status=self.NOT_FOUND)
        if not o.has_access(request.user):
            return self.response_forbidden("Access denied")
        # Run sa.wipe_managed_object job instead
        o.name = "wiping-%d" % o.id
        o.is_managed = False
        o.description = "Wiping! Do not touch!"
        o.save()
        call_later("noc.sa.wipe.managedobject.wipe", o=o.id)
        return HttpResponse(status=self.DELETED)

    @view(
        url=r"^actions/run_discovery/$",
        method=["POST"],
        access="launch",
        api=True,
        validate={
            "ids":
            ListOfParameter(element=ModelParameter(ManagedObject),
                            convert=True)
        },
    )
    def api_action_run_discovery(self, request, ids):
        d = 0
        for o in ids:
            if not o.has_access(request.user):
                continue
            o.run_discovery(delta=d)
            d += 1
        return "Discovery processes has been scheduled"

    def get_nested_inventory(self, o):
        rev = o.get_data("asset", "revision")
        if rev == "None":
            rev = ""
        r = {
            "id": str(o.id),
            "serial": o.get_data("asset", "serial"),
            "revision": rev or "",
            "description": o.model.description,
            "model": o.model.name,
        }
        children = []
        for n in o.model.connections:
            if n.direction == "i":
                c, r_object, _ = o.get_p2p_connection(n.name)
                if c is None:
                    children += [{
                        "id": None,
                        "name": n.name,
                        "leaf": True,
                        "serial": None,
                        "description": "--- EMPTY ---",
                        "model": None,
                    }]
                else:
                    cc = self.get_nested_inventory(r_object)
                    cc["name"] = n.name
                    children += [cc]
            elif n.direction == "s":
                children += [{
                    "id": None,
                    "name": n.name,
                    "leaf": True,
                    "serial": None,
                    "description": n.description,
                    "model": ", ".join(n.protocols),
                }]
        if children:
            to_expand = "Transceiver" not in o.model.name
            r["children"] = children
            r["expanded"] = to_expand
        else:
            r["leaf"] = True
        return r

    @view(url=r"^(?P<id>\d+)/inventory/$",
          method=["GET"],
          access="read",
          api=True)
    def api_inventory(self, request, id):
        o = self.get_object_or_404(ManagedObject, id=id)
        if not o.has_access(request.user):
            return self.response_forbidden("Access denied")
        r = []
        for p in o.get_inventory():
            c = self.get_nested_inventory(p)
            c["name"] = p.name or o.name
            r += [c]
        return {"expanded": True, "children": r}

    @view(url=r"^(?P<id>\d+)/confdb/$",
          method=["GET"],
          access="config",
          api=True)
    def api_confdb(self, request, id):
        o = self.get_object_or_404(ManagedObject, id=id)
        if not o.has_access(request.user):
            return self.response_forbidden("Access denied")
        cleanup = True
        if "cleanup" in request.GET:
            c = request.GET["cleanup"].strip().lower()
            cleanup = c not in ("no", "false", "0")
        cdb = o.get_confdb(cleanup=cleanup)
        return self.render_plain_text(cdb.dump("json"),
                                      content_type="text/json")

    @view(
        url=r"^(?P<id>\d+)/confdb/$",
        method=["POST"],
        validate={
            "query": StringParameter(),
            "cleanup": BooleanParameter(default=True),
            "dump": BooleanParameter(default=False),
        },
        access="config",
        api=True,
    )
    def api_confdb_query(self,
                         request,
                         id,
                         query="",
                         cleanup=True,
                         dump=False):
        o = self.get_object_or_404(ManagedObject, id=id)
        if not o.has_access(request.user):
            return self.response_forbidden("Access denied")
        cdb = o.get_confdb(cleanup=cleanup)
        try:
            r = list(cdb.query(query))
            result = {"status": True, "result": r}
            if dump:
                result["confdb"] = ujson.loads(cdb.dump("json"))
        except SyntaxError as e:
            result = {"status": False, "error": str(e)}
        return result

    @view(url=r"^(?P<id>\d+)/job_log/(?P<job>\S+)/$",
          method=["GET"],
          access="read",
          api=True)
    def api_job_log(self, request, id, job):
        o = self.get_object_or_404(ManagedObject, id=id)
        if not o.has_access(request.user):
            return self.response_forbidden("Access denied")
        # fs = gridfs.GridFS(get_db(), "noc.joblog")
        key = "discovery-%s-%s" % (job, o.id)
        d = get_db()["noc.joblog"].find_one({"_id": key})
        if d and d["log"]:
            return self.render_plain_text(zlib.decompress(str(d["log"])))
        else:
            return self.render_plain_text("No data")

    @view(url=r"^(?P<id>\d+)/interactions/$",
          method=["GET"],
          access="interactions",
          api=True)
    def api_interactions(self, request, id):
        o = self.get_object_or_404(ManagedObject, id=id)
        if not o.has_access(request.user):
            return self.response_forbidden("Access denied")
        return [{
            "ts": self.to_json(i.timestamp),
            "op": i.op,
            "user": i.user,
            "text": i.text
        } for i in InteractionLog.objects.filter(
            object=o.id).order_by("-timestamp")]

    @view(url=r"^(?P<id>\d+)/scripts/$",
          method=["GET"],
          access="script",
          api=True)
    def api_scripts(self, request, id):
        o = self.get_object_or_404(ManagedObject, id=id)
        if not o.has_access(request.user):
            return self.response_forbidden("Access denied")
        r = []
        for s in o.scripts:
            sn = o.profile.name + "." + s
            script = script_loader.get_script(sn)
            if not script:
                self.logger.error("Failed to load script: %s", sn)
                continue
            interface = script.interface()
            ss = {
                "name":
                s,
                "has_input":
                any(interface.gen_parameters()),
                "require_input":
                interface.has_required_params,
                "form":
                interface.get_form(),
                "preview":
                interface.preview
                or "NOC.sa.managedobject.scripts.JSONPreview",
            }
            r += [ss]
        return r

    @view(url=r"^(?P<id>\d+)/scripts/(?P<name>[^/]+)/$",
          method=["POST"],
          access="script",
          api=True)
    def api_run_script(self, request, id, name):
        o = self.get_object_or_404(ManagedObject, id=id)
        if not o.has_access(request.user):
            return {"error": "Access denied"}
        if name not in o.scripts:
            return {"error": "Script not found: %s" % name}
        params = self.deserialize(request.body)
        try:
            result = o.scripts[name](**params)
        except Exception as e:
            return {"error": str(e)}
        return {"result": result}

    @view(url=r"^(?P<id>\d+)/console/$",
          method=["POST"],
          access="console",
          api=True)
    def api_console_command(self, request, id):
        o = self.get_object_or_404(ManagedObject, id=id)
        if not o.has_access(request.user):
            return {"error": "Access denied"}
        if "commands" not in o.scripts:
            return {"error": "Script not found: commands"}
        params = self.deserialize(request.body)
        try:
            result = o.scripts.commands(**params)
        except Exception as e:
            return {"error": str(e)}
        return {"result": result}

    @view(url=r"(?P<id>\d+)/caps/$", method=["GET"], access="read", api=True)
    def api_get_caps(self, request, id):
        o = self.get_object_or_404(ManagedObject, id=id)
        if not o.has_access(request.user):
            return self.response_forbidden("Access denied")
        r = []
        oc = ObjectCapabilities.objects.filter(object=o).first()
        if oc:
            for c in oc.caps:
                r += [{
                    "capability": c.capability.name,
                    "description": c.capability.description,
                    "type": c.capability.type,
                    "value": c.value,
                    "source": c.source,
                }]
        return sorted(r, key=lambda x: x["capability"])

    @view(url=r"(?P<id>\d+)/facts/$", method=["GET"], access="read", api=True)
    def api_get_facts(self, request, id):
        o = self.get_object_or_404(ManagedObject, id=id)
        if not o.has_access(request.user):
            return self.response_forbidden("Access denied")
        return sorted(
            ({
                "cls": f.cls,
                "label": f.label,
                "attrs": [{
                    "name": a,
                    "value": f.attrs[a]
                } for a in f.attrs],
                "introduced": f.introduced.isoformat(),
                "changed": f.changed.isoformat(),
            } for f in ObjectFact.objects.filter(object=o.id)),
            key=lambda x: (x["cls"], x["label"]),
        )

    @view(url=r"(?P<id>\d+)/revalidate/$",
          method=["POST"],
          access="read",
          api=True)
    def api_revalidate(self, request, id):
        def revalidate(o):
            engine = Engine(o)
            engine.check()
            return self.response({"status": True}, self.OK)

        o = self.get_object_or_404(ManagedObject, id=id)
        if not o.has_access(request.user):
            return self.response_forbidden("Access denied")
        return self.submit_slow_op(request, revalidate, o)

    @view(url=r"(?P<id>\d+)/actions/(?P<action>\S+)/$",
          method=["POST"],
          access="action",
          api=True)
    def api_action(self, request, id, action):
        def execute(o, a, args):
            return a.execute(o, **args)

        o = self.get_object_or_404(ManagedObject, id=id)
        if not o.has_access(request.user):
            return self.response_forbidden("Access denied")
        a = self.get_object_or_404(Action, name=action)
        # @todo: Check access
        body = request.body
        if body:
            args = ujson.loads(body)
        else:
            args = {}
        return self.submit_slow_op(request, execute, o, a, args)

    @view(url=r"^link/fix/(?P<link_id>[0-9a-f]{24})/$",
          method=["POST"],
          access="change_link")
    def api_fix_links(self, request, link_id):
        def get_mac(arp, ip):
            for r in arp:
                if r["ip"] == ip:
                    return r["mac"]
            return None

        def get_interface(macs, mac):
            for m in macs:
                if m["mac"] == mac:
                    return m["interfaces"][0]
            return None

        def error_status(message, *args):
            self.logger.error(message, *args)
            return {"status": False, "message": message % args}

        def success_status(message, *args):
            self.logger.error(message, *args)
            return {"status": True, "message": message % args}

        link = self.get_object_or_404(Link, id=link_id)
        if len(link.interfaces) != 2:
            return error_status("Cannot fix link: Not P2P")
        mo1 = link.interfaces[0].managed_object
        mo2 = link.interfaces[1].managed_object
        if mo1.id == mo2.id:
            return error_status("Cannot fix circular links")
        # Ping each other
        self.logger.info("[%s] Pinging %s", mo1.name, mo2.address)
        r1 = mo1.scripts.ping(address=mo2.address)
        if not r1["success"]:
            return error_status("Failed to ping %s", mo2.name)
        self.logger.info("[%s] Pinging %s", mo2.name, mo1.address)
        r2 = mo2.scripts.ping(address=mo1.address)
        if not r2["success"]:
            return error_status("Failed to ping %s", mo1.name)
        # Get ARPs
        mac2 = get_mac(mo1.scripts.get_arp(), mo2.address)
        if not mac2:
            return error_status("[%s] ARP cache is not filled properly",
                                mo1.name)
        self.logger.info("[%s] MAC=%s", mo2.name, mac2)
        mac1 = get_mac(mo2.scripts.get_arp(), mo1.address)
        if not mac1:
            return error_status("[%s] ARP cache is not filled properly",
                                mo2.name)
        self.logger.info("[%s] MAC=%s", mo1.name, mac1)
        # Get MACs
        r1 = mo1.scripts.get_mac_address_table(mac=mac2)
        self.logger.info("[%s] MACS=%s", mo1.name, r1)
        r2 = mo2.scripts.get_mac_address_table(mac=mac1)
        self.logger.info("[%s] MACS=%s", mo2.name, r2)
        # mo1: Find mo2
        i1 = get_interface(r1, mac2)
        if not i1:
            return error_status("[%s] Cannot find %s in the MAC address table",
                                mo1.name, mo2.name)
        # mo2: Find mo1
        i2 = get_interface(r2, mac1)
        if not i1:
            return error_status("[%s] Cannot find %s in the MAC address table",
                                mo2.name, mo1.name)
        self.logger.info("%s:%s -- %s:%s", mo1.name, i1, mo2.name, i2)
        if link.interfaces[0].name == i1 and link.interfaces[1].name == i2:
            return success_status("Linked properly")
        # Get interfaces
        iface1 = mo1.get_interface(i1)
        if not iface1:
            return error_status("[%s] Interface not found: %s", mo1.name, i1)
        iface2 = mo2.get_interface(i2)
        if not iface2:
            return error_status("[%s] Interface not found: %s", mo2.name, i2)
        # Check we can relink
        if_ids = [i.id for i in link.interfaces]
        if iface1.id not in if_ids and iface1.is_linked:
            return error_status("[%s] %s is already linked", mo1.name, i1)
        if iface2.id not in if_ids and iface2.is_linked:
            return error_status("[%s] %s is already linked", mo2.name, i2)
        # Relink
        self.logger.info("Relinking")
        link.delete()
        iface1.link_ptp(iface2, method="macfix")
        return success_status("Relinked")

    @view(url=r"^(?P<id>\d+)/cpe/$", method=["GET"], access="read", api=True)
    def api_cpe(self, request, id):
        """
        GET CPEs
        :param request:
        :param id:
        :return:
        """
        def sorted_iname(s):
            return sorted(s, key=lambda x: split_alnum(x["name"]))

        # Get object
        o = self.get_object_or_404(ManagedObject, id=int(id))
        if not o.has_access(request.user):
            return self.response_forbidden("Permission denied")
        # CPE
        # @todo: proper ordering
        # default_state = ResourceState.get_default()
        # style_cache = {}  # profile_id -> css_style
        l1 = [
            {
                "global_id": str(c.global_id),
                "name": c.name or "",
                "interface": c.interface,
                "local_id": c.local_id,
                "serial": c.serial or "",
                "status": c.status,
                "description": c.description or "",
                "address": c.ip or "",
                "model": c.model or "",
                "version": c.version or "",
                "mac": c.mac or "",
                "location": c.location or "",
                "distance": str(c.distance)
                # "row_class": get_style(i)
            } for c in CPEStatus.objects.filter(managed_object=o.id)
        ]

        return {"cpe": sorted_iname(l1)}
Esempio n. 12
0
class PeerApplication(ExtModelApplication):
    """
    Peers application
    """
    title = "Peers"
    menu = "Peers"
    model = Peer
    query_fields = [
        "remote_asn__icontains", "description__icontains",
        "local_ip__icontains", "local_backup_ip__icontains",
        "remote_ip__icontains", "remote_backup_ip__icontains"
    ]

    def clean(self, data):
        data = super(PeerApplication, self).clean(data)
        ## Check address fields
        if not is_prefix(data["local_ip"]):
            raise ValueError(
                "Invalid 'Local IP Address', must be in x.x.x.x/x form or IPv6 prefix"
            )
        if not is_prefix(data["remote_ip"]):
            raise ValueError(
                "Invalid 'Remote IP Address', must be in x.x.x.x/x form or IPv6 prefix"
            )
        if "local_backup_ip" in data and data["local_backup_ip"]:
            if not is_prefix(data["local_backup_ip"]):
                raise ValueError(
                    "Invalid 'Local Backup IP Address', must be in x.x.x.x/x form or IPv6 prefix"
                )
        if "remote_backup_ip" in data and data["remote_backup_ip"]:
            if not is_prefix(data["remote_backup_ip"]):
                raise ValueError(
                    "Invalid 'Remote Backup IP Address', must be in x.x.x.x/x form or IPv6 prefix"
                )

        ## Check no or both backup addresses given
        has_local_backup = "local_backup_ip" in data and data["local_backup_ip"]
        has_remote_backup = "remote_backup_ip" in data and data[
            "remote_backup_ip"]
        if has_local_backup and not has_remote_backup:
            raise ValueError("One of backup addresses given. Set peer address")
        if not has_local_backup and has_remote_backup:
            raise ValueError("One of backup addresses given. Set peer address")
        ## Check all link addresses belongs to one AFI
        if len(
                set([
                    IP.prefix(data[x]).afi for x in [
                        "local_ip", "remote_ip", "local_backup_ip",
                        "remote_backup_ip"
                    ] if x in data and data[x]
                ])) > 1:
            raise ValueError(
                "All neighboring addresses must have same address family")
        return data

    ##
    ## Change peer status
    ##
    def set_peer_status(self, request, queryset, status, message):
        count = 0
        for p in queryset:
            p.status = status
            p.save()
            count += 1
        if count == 1:
            return "1 peer marked as %s" % message
        else:
            return "%d peers marked as %s" % (count, message)

    @view(url="^actions/planned/$",
          method=["POST"],
          access="update",
          api=True,
          validate={"ids": ListOfParameter(element=ModelParameter(Peer))})
    def api_action_planned(self, request, ids):
        return self.set_peer_status(request, ids, "P", "planned")

    api_action_planned.short_description = "Mark as planned"

    @view(url="^actions/active/$",
          method=["POST"],
          access="update",
          api=True,
          validate={"ids": ListOfParameter(element=ModelParameter(Peer))})
    def api_action_active(self, request, ids):
        return self.set_peer_status(request, ids, "A", "active")

    api_action_active.short_description = "Mark as active"

    @view(url="^actions/shutdown/$",
          method=["POST"],
          access="update",
          api=True,
          validate={"ids": ListOfParameter(element=ModelParameter(Peer))})
    def api_action_shutdown(self, request, ids):
        return self.set_peer_status(request, ids, "S", "shutdown")

    api_action_shutdown.short_description = "Mark as shutdown"
Esempio n. 13
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
Esempio n. 14
0
class VCApplication(ExtModelApplication):
    """
    VC application
    """

    title = _("VC")
    menu = _("Virtual Circuits")
    model = VC

    query_fields = ["name", "description"]
    query_condition = "icontains"
    int_query_fields = ["l1", "l2"]

    implied_permissions = {"read": ["vc:vcdomain:lookup", "main:style:lookup"]}

    def get_vc_domain_objects(self, vc_domain):
        return vc_domain.managedobject_set.all()

    def lookup_vcfilter(self, q, name, value):
        """
        Resolve __vcflter lookups
        :param q:
        :param name:
        :param value:
        :return:
        """
        value = ModelParameter(VCFilter).clean(value)
        x = value.to_sql(name)
        try:
            q[None] += [x]
        except KeyError:
            q[None] = [x]

    @cachedmethod(key="vc-interface-count-%s")
    def get_vc_interfaces_count(self, vc_id):
        vc = VC.get_by_id(vc_id)
        if not vc:
            return 0
        objects = vc.vc_domain.managedobject_set.values_list("id", flat=True)
        l1 = vc.l1
        n = SubInterface.objects.filter(
            Q(managed_object__in=objects)
            & (
                Q(untagged_vlan=l1, enabled_afi=["BRIDGE"])
                | Q(tagged_vlans=l1, enabled_afi=["BRIDGE"])
                | Q(vlan_ids=l1)
            )
        ).count()
        return n

    @cachedmethod(key="vc-prefixes-%s")
    def get_vc_prefixes(self, vc_id):
        vc = VC.get_by_id(vc_id)
        if not vc:
            return []
        objects = vc.vc_domain.managedobject_set.values_list("id", flat=True)
        ipv4 = set()
        ipv6 = set()
        # @todo: Exact match on vlan_ids
        for si in SubInterface.objects.filter(
            Q(managed_object__in=objects)
            & Q(vlan_ids=vc.l1)
            & (Q(enabled_afi=["IPv4"]) | Q(enabled_afi=["IPv6"]))
        ).only("enabled_afi", "ipv4_addresses", "ipv6_addresses"):
            if "IPv4" in si.enabled_afi:
                ipv4.update([IP.prefix(ip).first for ip in si.ipv4_addresses])
            if "IPv6" in si.enabled_afi:
                ipv6.update([IP.prefix(ip).first for ip in si.ipv6_addresses])
        p = [str(x.first) for x in sorted(ipv4)]
        p += [str(x.first) for x in sorted(ipv6)]
        return p

    def field_interfaces_count(self, obj):
        return self.get_vc_interfaces_count(obj.id)

    def field_prefixes(self, obj):
        p = self.get_vc_prefixes(obj.id)
        if p:
            return ", ".join(p)
        else:
            return "-"

    def field_row_class(self, o):
        return o.style.css_class_name if o.style else ""

    @view(
        url="^find_free/$",
        method=["GET"],
        access="read",
        api=True,
        validate={"vc_domain": ModelParameter(VCDomain), "vc_filter": ModelParameter(VCFilter)},
    )
    def api_find_free(self, request, vc_domain, vc_filter, **kwargs):
        return vc_domain.get_free_label(vc_filter)

    @view(
        url="^bulk/import/",
        method=["POST"],
        access="import",
        api=True,
        validate={
            "vc_domain": ModelParameter(VCDomain),
            "items": ListOfParameter(
                element=DictParameter(
                    attrs={
                        "l1": IntParameter(),
                        "l2": IntParameter(),
                        "name": StringParameter(),
                        "description": StringParameter(default=""),
                    }
                )
            ),
        },
    )
    def api_bulk_import(self, request, vc_domain, items):
        n = 0
        for i in items:
            if not VC.objects.filter(vc_domain=vc_domain, l1=i["l1"], l2=i["l2"]).exists():
                # Add only not-existing
                VC(
                    vc_domain=vc_domain,
                    l1=i["l1"],
                    l2=i["l2"],
                    name=i["name"],
                    description=i["description"],
                ).save()
                n += 1
        return {"status": True, "imported": n}

    @view(url=r"^(?P<vc_id>\d+)/interfaces/$", method=["GET"], access="read", api=True)
    def api_interfaces(self, request, vc_id):
        """
        Returns a dict of {untagged: ..., tagged: ...., l3: ...}
        :param request:
        :param vc_id:
        :return:
        """
        vc = self.get_object_or_404(VC, id=int(vc_id))
        l1 = vc.l1
        # Managed objects in VC domain
        objects = set(vc.vc_domain.managedobject_set.values_list("id", flat=True))
        # Find untagged interfaces
        si_objects = defaultdict(list)
        for si in SubInterface.objects.filter(
            managed_object__in=objects, untagged_vlan=l1, enabled_afi="BRIDGE"
        ):
            si_objects[si.managed_object] += [{"name": si.name}]
        untagged = [
            {
                "managed_object_id": o.id,
                "managed_object_name": o.name,
                "interfaces": sorted(si_objects[o], key=lambda x: x["name"]),
            }
            for o in si_objects
        ]
        # Find tagged interfaces
        si_objects = defaultdict(list)
        for si in SubInterface.objects.filter(
            managed_object__in=objects, tagged_vlans=l1, enabled_afi="BRIDGE"
        ):
            si_objects[si.managed_object] += [{"name": si.name}]
        tagged = [
            {
                "managed_object_id": o.id,
                "managed_object_name": o.name,
                "interfaces": sorted(si_objects[o], key=lambda x: x["name"]),
            }
            for o in si_objects
        ]
        # Find l3 interfaces
        si_objects = defaultdict(list)
        for si in SubInterface.objects.filter(managed_object__in=objects, vlan_ids=l1):
            si_objects[si.managed_object] += [
                {
                    "name": si.name,
                    "ipv4_addresses": si.ipv4_addresses,
                    "ipv6_addresses": si.ipv6_addresses,
                }
            ]
        l3 = [
            {
                "managed_object_id": o.id,
                "managed_object_name": o.name,
                "interfaces": sorted(si_objects[o], key=lambda x: x["name"]),
            }
            for o in si_objects
        ]
        # Update caches
        ic = sum(len(x["interfaces"]) for x in untagged)
        ic += sum(len(x["interfaces"]) for x in tagged)
        ic += sum(len(x["interfaces"]) for x in l3)
        #
        return {
            "untagged": sorted(untagged, key=lambda x: x["managed_object_name"]),
            "tagged": sorted(tagged, key=lambda x: x["managed_object_name"]),
            "l3": sorted(l3, key=lambda x: x["managed_object_name"]),
        }
Esempio n. 15
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
Esempio n. 16
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
Esempio n. 17
0
File: views.py Progetto: nbashev/noc
class InterfaceAppplication(ExtApplication):
    """
    inv.interface application
    """

    title = _("Interfaces")
    menu = _("Interfaces")

    mrt_config = {
        "get_mac": {
            "map_script": "get_mac_address_table",
            "timeout": config.script.timeout,
            "access": "get_mac",
        }
    }

    implied_permissions = {
        "get_mac": [
            "inv:inv:read",
            "inv:interface:view",
            "sa:managedobject:lookup",
            "sa:managedobject:read",
        ]
    }

    @view(url=r"^(?P<managed_object>\d+)/$",
          method=["GET"],
          access="view",
          api=True)
    def api_get_interfaces(self, request, managed_object):
        """
        GET interfaces
        :param managed_object:
        :return:
        """
        def sorted_iname(s):
            return list(sorted(s, key=lambda x: alnum_key(x["name"])))

        def get_style(i):
            profile = i.profile
            if profile:
                try:
                    return style_cache[profile.id]
                except KeyError:
                    pass
                if profile.style:
                    s = profile.style.css_class_name
                else:
                    s = ""
                style_cache[profile.id] = s
                return s
            else:
                return ""

        def get_link(i):
            link = i.link
            if not link:
                return None
            if link.is_ptp:
                # ptp
                o = link.other_ptp(i)
                label = "%s:%s" % (o.managed_object.name, o.name)
            elif link.is_lag:
                # unresolved LAG
                o = [
                    ii for ii in link.other(i)
                    if ii.managed_object.id != i.managed_object.id
                ]
                label = "LAG %s: %s" % (o[0].managed_object.name, ", ".join(
                    ii.name for ii in o))
            else:
                # Broadcast
                label = ", ".join("%s:%s" % (ii.managed_object.name, ii.name)
                                  for ii in link.other(i))
            return {"id": str(link.id), "label": label}

        # Get object
        o = self.get_object_or_404(ManagedObject, id=int(managed_object))
        if not o.has_access(request.user):
            return self.response_forbidden("Permission denied")
        # Physical interfaces
        # @todo: proper ordering
        default_state = ResourceState.get_default()
        style_cache = {}  # profile_id -> css_style
        l1 = [{
            "id":
            str(i.id),
            "name":
            i.name,
            "description":
            i.description,
            "mac":
            i.mac,
            "ifindex":
            i.ifindex,
            "lag":
            (i.aggregated_interface.name if i.aggregated_interface else ""),
            "link":
            get_link(i),
            "profile":
            str(i.profile.id) if i.profile else None,
            "profile__label":
            smart_text(i.profile) if i.profile else None,
            "enabled_protocols":
            i.enabled_protocols,
            "project":
            i.project.id if i.project else None,
            "project__label":
            smart_text(i.project) if i.project else None,
            "state":
            i.state.id if i.state else default_state.id,
            "state__label":
            smart_text(i.state if i.state else default_state),
            "vc_domain":
            i.vc_domain.id if i.vc_domain else None,
            "vc_domain__label":
            smart_text(i.vc_domain) if i.vc_domain else None,
            "row_class":
            get_style(i),
        } for i in Interface.objects.filter(managed_object=o.id,
                                            type="physical")]
        # LAG
        lag = [{
            "id":
            str(i.id),
            "name":
            i.name,
            "description":
            i.description,
            "members": [
                j.name
                for j in Interface.objects.filter(managed_object=o.id,
                                                  aggregated_interface=i.id)
            ],
            "profile":
            str(i.profile.id) if i.profile else None,
            "profile__label":
            smart_text(i.profile) if i.profile else None,
            "enabled_protocols":
            i.enabled_protocols,
            "project":
            i.project.id if i.project else None,
            "project__label":
            smart_text(i.project) if i.project else None,
            "state":
            i.state.id if i.state else default_state.id,
            "state__label":
            smart_text(i.state if i.state else default_state),
            "vc_domain":
            i.vc_domain.id if i.vc_domain else None,
            "vc_domain__label":
            smart_text(i.vc_domain) if i.vc_domain else None,
            "row_class":
            get_style(i),
        } for i in Interface.objects.filter(managed_object=o.id,
                                            type="aggregated")]
        # L2 interfaces
        l2 = [{
            "name": i.name,
            "description": i.description,
            "untagged_vlan": i.untagged_vlan,
            "tagged_vlans": i.tagged_vlans,
        } for i in SubInterface.objects.filter(managed_object=o.id,
                                               enabled_afi="BRIDGE")]
        # L3 interfaces
        q = Q(enabled_afi="IPv4") | Q(enabled_afi="IPv6")
        l3 = [{
            "name":
            i.name,
            "description":
            i.description,
            "ipv4_addresses":
            i.ipv4_addresses,
            "ipv6_addresses":
            i.ipv6_addresses,
            "enabled_protocols":
            i.enabled_protocols,
            "vlan":
            i.vlan_ids,
            "vrf":
            i.forwarding_instance.name if i.forwarding_instance else "",
        } for i in SubInterface.objects.filter(managed_object=o.id).filter(q)]
        return {
            "l1": sorted_iname(l1),
            "lag": sorted_iname(lag),
            "l2": sorted_iname(l2),
            "l3": sorted_iname(l3),
        }

    @view(
        url=r"^link/$",
        method=["POST"],
        validate={
            "type": StringParameter(choices=["ptp"]),
            "interfaces":
            ListOfParameter(element=DocumentParameter(Interface)),
        },
        access="link",
        api=True,
    )
    def api_link(self, request, type, interfaces):
        if type == "ptp":
            if len(interfaces) == 2:
                interfaces[0].link_ptp(interfaces[1])
                return {"status": True}
            else:
                raise ValueError("Invalid interfaces length")
        return {"status": False}

    @view(url=r"^unlink/(?P<iface_id>[0-9a-f]{24})/$",
          method=["POST"],
          access="link",
          api=True)
    def api_unlink(self, request, iface_id):
        i = Interface.objects.filter(id=iface_id).first()
        if not i:
            return self.response_not_found()
        try:
            i.unlink()
            return {"status": True, "msg": "Unlinked"}
        except ValueError as why:
            return {"status": False, "msg": str(why)}

    @view(url=r"^unlinked/(?P<object_id>\d+)/$",
          method=["GET"],
          access="link",
          api=True)
    def api_unlinked(self, request, object_id):
        def get_label(i):
            if i.description:
                return "%s (%s)" % (i.name, i.description)
            else:
                return i.name

        o = self.get_object_or_404(ManagedObject, id=int(object_id))
        r = [{
            "id": str(i.id),
            "label": get_label(i)
        } for i in Interface.objects.filter(managed_object=o.id,
                                            type="physical").order_by("name")
             if not i.link]
        return list(sorted(r, key=lambda x: alnum_key(x["label"])))

    @view(
        url=r"^l1/(?P<iface_id>[0-9a-f]{24})/change_profile/$",
        validate={"profile": DocumentParameter(InterfaceProfile)},
        method=["POST"],
        access="profile",
        api=True,
    )
    def api_change_profile(self, request, iface_id, profile):
        i = Interface.objects.filter(id=iface_id).first()
        if not i:
            return self.response_not_found()
        if i.profile != profile:
            i.profile = profile
            i.profile_locked = True
            i.save()
        return True

    @view(
        url=r"^l1/(?P<iface_id>[0-9a-f]{24})/change_state/$",
        validate={"state": ModelParameter(ResourceState)},
        method=["POST"],
        access="profile",
        api=True,
    )
    def api_change_state(self, request, iface_id, state):
        i = Interface.objects.filter(id=iface_id).first()
        if not i:
            return self.response_not_found()
        if i.state != state:
            i.state = state
            i.save()
        return True

    @view(
        url=r"^l1/(?P<iface_id>[0-9a-f]{24})/change_project/$",
        validate={"project": ModelParameter(Project, required=False)},
        method=["POST"],
        access="profile",
        api=True,
    )
    def api_change_project(self, request, iface_id, project):
        i = Interface.objects.filter(id=iface_id).first()
        if not i:
            return self.response_not_found()
        if i.project != project:
            i.project = project
            i.save()
        return True

    @view(
        url=r"^l1/(?P<iface_id>[0-9a-f]{24})/change_vc_domain/$",
        validate={"vc_domain": ModelParameter(VCDomain, required=False)},
        method=["POST"],
        access="profile",
        api=True,
    )
    def api_change_vc_domain(self, request, iface_id, vc_domain):
        i = Interface.objects.filter(id=iface_id).first()
        if not i:
            return self.response_not_found()
        if i.vc_domain != vc_domain:
            i.vc_domain = vc_domain
            i.save()
        return True
Esempio n. 18
0
 def cleaned_query(self, q):
     nq = {}
     q = q.copy()
     # Extract IN
     # extjs not working with same parameter name in query
     for p in list(q.keys()):
         if p.endswith(self.in_param):
             match = self.rx_oper_splitter.match(p)
             if match:
                 field = self.rx_oper_splitter.match(p).group("field") + self.in_param
                 if field not in q:
                     q[field] = "%s" % (q[p])
                 else:
                     q[field] += ",%s" % (q[p])
                 del q[p]
     for p in q:
         if p.endswith("__exists"):
             v = BooleanParameter().clean(q[p])
             nq[p.replace("__exists", "__isnull")] = not v
             continue
         if "__" in p:
             np, lt = p.split("__", 1)
         else:
             np, lt = p, None
             # Skip ignored params
         if np in self.ignored_params or p in (
             self.limit_param,
             self.page_param,
             self.start_param,
             self.format_param,
             self.sort_param,
             self.group_param,
             self.query_param,
             self.only_param,
         ):
             continue
         v = q[p]
         if self.in_param in p:
             v = v.split(",")
         if v == "\x00":
             v = None
         # Pass through interface cleaners
         if lt == "referred":
             # Unroll __referred
             app, fn = v.split("__", 1)
             model = self.site.apps[app].model
             if not is_document(model):
                 extra_where = '%s."%s" IN (SELECT "%s" FROM %s)' % (
                     self.model._meta.db_table,
                     self.model._meta.pk.name,
                     model._meta.get_field(fn).attname,
                     model._meta.db_table,
                 )
                 if None in nq:
                     nq[None] += [extra_where]
                 else:
                     nq[None] = [extra_where]
             continue
         elif lt and hasattr(self, "lookup_%s" % lt):
             # Custom lookup
             getattr(self, "lookup_%s" % lt)(nq, np, v)
             continue
         elif np in self.fk_fields and lt:
             # dereference
             try:
                 nq[np] = self.fk_fields[np].objects.get(**{lt: v})
             except self.fk_fields[np].DoesNotExist:
                 nq[np] = 0  # False search
             continue
         elif np in self.clean_fields and self.in_param in p:
             v = ListOfParameter(self.clean_fields[np]).clean(v)
         elif np in self.clean_fields:  # @todo: Check for valid lookup types
             v = self.clean_fields[np].clean(v)
             # Write back
         nq[p] = v
     return nq
Esempio n. 19
0
class RunCommandsApplication(ExtApplication):
    title = _("Run Commands")
    menu = [_("Run Commands")]

    implied_permissions = {"launch": ["sa:objectlist:read"]}

    @view(url=r"^form/snippet/(?P<snippet_id>\d+)/$",
          method=["GET"],
          access="launch",
          api=True)
    def api_form_snippet(self, request, snippet_id):
        snippet = self.get_object_or_404(CommandSnippet, id=int(snippet_id))
        r = []
        vars = snippet.vars
        for k in vars:
            cfg = {
                "name": k,
                "fieldLabel": k,
                "allowBlank": not vars[k].get("required", False)
            }
            t = vars[k].get("type")
            if t == "int":
                cfg["xtype"] = "numberfield"
            else:
                cfg["xtype"] = "textfield"
            r += [cfg]
        return r

    @view(url=r"^form/action/(?P<action_id>[0-9a-f]{24})/$",
          method=["GET"],
          access="launch",
          api=True)
    def api_form_action(self, request, action_id):
        action = self.get_object_or_404(Action, id=action_id)
        r = []
        for p in action.params:
            cfg = {
                "name": p.name,
                "fieldLabel": p.description or p.name,
                "allowBlank": not p.is_required,
            }
            if p.type == "int":
                cfg["xtype"] = "numberfield"
            else:
                cfg["xtype"] = "textfield"
            r += [cfg]
        return r

    @view(
        url=r"^render/snippet/(?P<snippet_id>\d+)/$",
        method=["POST"],
        validate={
            "objects": ListOfParameter(element=ModelParameter(ManagedObject)),
            "config": DictParameter(),
        },
        access="launch",
        api=True,
    )
    def api_render_snippet(self, request, snippet_id, objects, config):
        snippet = self.get_object_or_404(CommandSnippet, id=int(snippet_id))
        r = {}
        for mo in objects:
            config["object"] = mo
            r[mo.id] = snippet.expand(config)
        return r

    @view(
        url=r"^render/action/(?P<action_id>[0-9a-f]{24})/$",
        method=["POST"],
        validate={
            "objects": ListOfParameter(element=ModelParameter(ManagedObject)),
            "config": DictParameter(),
        },
        access="launch",
        api=True,
    )
    def api_render_action(self, request, action_id, objects, config):
        action = self.get_object_or_404(Action, id=action_id)
        r = {}
        for mo in objects:
            r[mo.id] = action.expand(mo, **config)
        return r
Esempio n. 20
0
    IntParameter,
    ListOfParameter,
    ListParameter,
)
from noc.pm.models.metrictype import MetricType
from ..base import NBIAPI


Request = DictParameter(
    attrs={
        "bi_id": IntParameter(required=True),
        "metrics": DictListParameter(
            attrs={
                "metric_type": StringParameter(required=True),
                "path": StringListParameter(required=True),
                "values": ListOfParameter(ListParameter(), required=True),
            },
            required=True,
        ),
    }
)


class TelemetryAPI(NBIAPI):
    name = "telemetry"

    @authenticated
    @tornado.gen.coroutine
    def post(self):
        code, result = yield self.executor.submit(self.handler)
        self.set_status(code)
Esempio n. 21
0
class UserProfileApplication(ExtApplication):
    """
    main.userprofile application
    """
    title = "User Profile"
    implied_permissions = {"launch": ["main:timepattern:lookup"]}

    @view(url="^$", method=["GET"], access=PermitLogged(), api=True)
    def api_get(self, request):
        user = request.user
        try:
            profile = user.get_profile()
            language = profile.preferred_language
            theme = profile.theme
            preview_theme = profile.preview_theme
            contacts = [{
                "time_pattern": c.time_pattern.id,
                "time_pattern__label": c.time_pattern.name,
                "notification_method": c.notification_method,
                "params": c.params
            } for c in profile.userprofilecontact_set.all()]
        except UserProfile.DoesNotExist:
            language = None
            theme = None
            preview_theme = None
            contacts = []
        return {
            "username":
            user.username,
            "name":
            (" ".join([x for x in (user.first_name, user.last_name)
                       if x])).strip(),
            "email":
            user.email,
            "preferred_language":
            language or "en",
            "theme":
            theme or "gray",
            "preview_theme":
            preview_theme or "midnight",
            "contacts":
            contacts
        }

    @view(url="^$",
          method=["POST"],
          access=PermitLogged(),
          api=True,
          validate={
              "preferred_language":
              StringParameter(choices=[x[0] for x in LANGUAGES]),
              "theme":
              StringParameter(),
              "preview_theme":
              StringParameter(),
              "contacts":
              ListOfParameter(element=DictParameter(
                  attrs={
                      "time_pattern":
                      ModelParameter(TimePattern),
                      "notification_method":
                      StringParameter(choices=[
                          x[0] for x in USER_NOTIFICATION_METHOD_CHOICES
                      ]),
                      "params":
                      StringParameter()
                  }))
          })
    def api_save(self, request, preferred_language, theme, preview_theme,
                 contacts):
        user = request.user
        try:
            profile = user.get_profile()
        except UserProfile.DoesNotExist:
            profile = UserProfile(user=user)
        profile.preferred_language = preferred_language
        profile.theme = theme
        profile.preview_theme = preview_theme
        profile.save()
        # Setup contacts
        for c in profile.userprofilecontact_set.all():
            c.delete()
        for c in contacts:
            UserProfileContact(user_profile=profile,
                               time_pattern=c["time_pattern"],
                               notification_method=c["notification_method"],
                               params=c["params"]).save()
        # Setup language
        request.session["django_lang"] = preferred_language
        return True
Esempio n. 22
0
def test_listof_parameter_error(raw, config):
    with pytest.raises(InterfaceTypeError):
        assert ListOfParameter(**config).clean(raw)
Esempio n. 23
0
class UserProfileApplication(ExtApplication):
    """
    main.userprofile application
    """

    title = _("User Profile")
    implied_permissions = {"launch": ["main:timepattern:lookup"]}

    @view(url="^$", method=["GET"], access=PermitLogged(), api=True)
    def api_get(self, request):
        user = request.user
        language = user.preferred_language
        contacts = [{
            "time_pattern": c.time_pattern.id,
            "time_pattern__label": c.time_pattern.name,
            "notification_method": c.notification_method,
            "params": c.params,
        } for c in UserContact.objects.filter(user=user)]
        return {
            "username":
            user.username,
            "name":
            (" ".join([x for x in (user.first_name, user.last_name)
                       if x])).strip(),
            "email":
            user.email,
            "preferred_language":
            language or "en",
            "contacts":
            contacts,
            "groups": [g.name for g in user.groups.all().order_by("name")],
        }

    @view(
        url="^$",
        method=["POST"],
        access=PermitLogged(),
        api=True,
        validate={
            "preferred_language":
            StringParameter(choices=[x[0] for x in LANGUAGES]),
            "contacts":
            ListOfParameter(element=DictParameter(
                attrs={
                    "time_pattern":
                    ModelParameter(TimePattern),
                    "notification_method":
                    StringParameter(choices=[
                        x[0] for x in USER_NOTIFICATION_METHOD_CHOICES
                    ]),
                    "params":
                    StringParameter(),
                })),
        },
    )
    def api_save(self, request, preferred_language, contacts):
        user = request.user
        user.preferred_language = preferred_language
        user.save()
        # Setup contacts
        UserContact.objects.filter(user=user).delete()
        for c in contacts:
            UserContact(
                user=user,
                time_pattern=c["time_pattern"],
                notification_method=c["notification_method"],
                params=c["params"],
            ).save()
        return True
Esempio n. 24
0
class ObjectModelApplication(ExtDocApplication):
    """
    ObjectModel application
    """
    title = "Object Models"
    menu = "Setup | Object Models"
    model = ObjectModel
    parent_model = DocCategory
    parent_field = "parent"
    query_fields = [
        "name__icontains", "description__icontains", "data__asset__part_no",
        "data__asset__order_part_no", "uuid"
    ]

    def clean(self, data):
        if "data" in data:
            data["data"] = ModelInterface.clean_data(data["data"])
        if "plugins" in data and data["plugins"]:
            data["plugins"] = [
                x.strip() for x in data["plugins"].split(",") if x.strip()
            ]
        else:
            data["plugins"] = None
        return super(ObjectModelApplication, self).clean(data)

    def cleaned_query(self, q):
        if "is_container" in q:
            q["data__container__container"] = True
            q["name__ne"] = "Root"
            del q["is_container"]
        return super(ObjectModelApplication, self).cleaned_query(q)

    @view(url="^(?P<id>[0-9a-f]{24})/compatible/$",
          method=["GET"],
          access="read",
          api=True)
    def api_compatible(self, request, id):
        o = self.get_object_or_404(ObjectModel, id=id)
        # Connections
        r = []
        for c in o.connections:
            # Find compatible objects
            proposals = []
            for t, n in o.get_connection_proposals(c.name):
                m = ObjectModel.objects.filter(id=t).first()
                mc = m.get_model_connection(n)
                proposals += [{
                    "model": m.name,
                    "model_description": m.description,
                    "name": n,
                    "description": mc.description,
                    "gender": mc.gender
                }]
            #
            if (r and r[-1]["direction"] == c.direction
                    and r[-1]["gender"] == c.gender
                    and r[-1]["connections"] == proposals):
                r[-1]["names"] += [{
                    "name": c.name,
                    "description": c.description
                }]
            else:
                r += [{
                    "names": [{
                        "name": c.name,
                        "description": c.description
                    }],
                    "direction": c.direction,
                    "gender": c.gender,
                    "connections": proposals
                }]
        # Crossing
        # @todo: Count splitter interface
        rc = []
        for c in o.connections:
            if c.cross:
                rc += [{"y": c.name, "x": c.cross, "v": "1"}]
        return {"connections": r, "crossing": rc}

    @view(url="^actions/json/$",
          method=["POST"],
          access="read",
          validate={
              "ids":
              ListOfParameter(element=DocumentParameter(ObjectModel),
                              convert=True)
          },
          api=True)
    def api_action_json(self, request, ids):
        r = [o.json_data for o in ids]
        s = to_json(r, order=["name", "vendor__code", "description"])
        return {"data": s}
Esempio n. 25
0
 def __init__(self, *args, **kwargs):
     super(ExtDocApplication, self).__init__(*args, **kwargs)
     self.pk = "id"  # @todo: detect properly
     self.has_uuid = False
     # Prepare field converters
     self.clean_fields = self.clean_fields.copy()  # name -> Parameter
     for name, f in six.iteritems(self.model._fields):
         if isinstance(f, BooleanField):
             self.clean_fields[name] = BooleanParameter()
         elif isinstance(f, GeoPointField):
             self.clean_fields[name] = GeoPointParameter()
         elif isinstance(f, ForeignKeyField):
             self.clean_fields[f.name] = ModelParameter(f.document_type, required=f.required)
         elif isinstance(f, ListField):
             if isinstance(f.field, EmbeddedDocumentField):
                 self.clean_fields[f.name] = ListOfParameter(
                     element=EmbeddedDocumentParameter(f.field.document_type)
                 )
         elif isinstance(f, ReferenceField):
             dt = f.document_type_obj
             if dt == "self":
                 dt = self.model
             self.clean_fields[f.name] = DocumentParameter(dt, required=f.required)
         if f.primary_key:
             self.pk = name
         if name == "uuid":
             self.has_uuid = True
     #
     if not self.query_fields:
         self.query_fields = [
             "%s__%s" % (n, self.query_condition)
             for n, f in six.iteritems(self.model._fields)
             if f.unique and isinstance(f, StringField)
         ]
     self.unique_fields = [n for n, f in six.iteritems(self.model._fields) if f.unique]
     # Install JSON API call when necessary
     self.json_collection = self.model._meta.get("json_collection")
     if (
         self.has_uuid
         and hasattr(self.model, "to_json")
         and not hasattr(self, "api_to_json")
         and not hasattr(self, "api_json")
     ):
         self.add_view(
             "api_json",
             self._api_to_json,
             url=r"^(?P<id>[0-9a-f]{24})/json/$",
             method=["GET"],
             access="read",
             api=True,
         )
         self.add_view(
             "api_share_info",
             self._api_share_info,
             url=r"^(?P<id>[0-9a-f]{24})/share_info/$",
             method=["GET"],
             access="read",
             api=True,
         )
     if self.json_collection:
         self.bulk_fields += [self._bulk_field_is_builtin]
     # Find field_* and populate custom fields
     self.custom_fields = {}
     for fn in [n for n in dir(self) if n.startswith("field_")]:
         h = getattr(self, fn)
         if callable(h):
             self.custom_fields[fn[6:]] = h
Esempio n. 26
0
class ManagedObjectApplication(ExtModelApplication):
    """
    ManagedObject application
    """
    title = "Managed Objects"
    menu = "Managed Objects"
    model = ManagedObject
    query_condition = "icontains"
    query_fields = ["name", "description", "address"]
    # Inlines
    attrs = ModelInline(ManagedObjectAttribute)
    cfg = RepoInline("config")

    extra_permissions = ["alarm", "change_interface"]

    mrt_config = {
        "console": {
            "access": "console",
            "map_script": "commands",
            "timeout": 60
        }
    }

    def field_platform(self, o):
        return o.platform

    def field_row_class(self, o):
        return o.object_profile.style.css_class_name if o.object_profile.style else ""

    def field_interface_count(self, o):
        return Interface.objects.filter(managed_object=o.id, type="physical").count()

    def field_link_count(self, o):
        return Link.object_links_count(o)

    def queryset(self, request, query=None):
        qs = super(ManagedObjectApplication, self).queryset(request, query)
        if not request.user.is_superuser:
            qs = qs.filter(UserAccess.Q(request.user))
        qs = qs.exclude(name__startswith="wiping-")
        return qs

    @view(url="^(?P<id>\d+)/links/$", method=["GET"],
          access="read", api=True)
    def api_links(self, request, id):
        o = self.get_object_or_404(ManagedObject, id=id)
        if not o.has_access(request.user):
            return self.response_forbidden("Access denied")
        # Get links
        result = []
        for link in Link.object_links(o):
            l = []
            r = []
            for i in link.interfaces:
                if i.managed_object.id == o.id:
                    l += [i]
                else:
                    r += [i]
                for li, ri in zip(l, r):
                    result += [{
                        "id": str(link.id),
                        "local_interface": str(li.id),
                        "local_interface__label": li.name,
                        "remote_object": ri.managed_object.id,
                        "remote_object__label": ri.managed_object.name,
                        "remote_interface": str(ri.id),
                        "remote_interface__label": ri.name,
                        "discovery_method": link.discovery_method,
                        "commited": True,
                        "local_description": li.description,
                        "remote_description": ri.description
                    }]
        # Get pending links
        q = MQ(local_object=o.id) | MQ(remote_object=o.id)
        for link in PendingLinkCheck.objects.filter(q):
            if link.local_object.id == o.id:
                ro = link.remote_object
                lin = link.local_interface
                rin = link.remote_interface
            else:
                ro = link.local_object
                lin = link.remote_interface
                rin = link.local_interface
            li = Interface.objects.filter(managed_object=o.id, name=lin).first()
            if not li:
                continue
            ri = Interface.objects.filter(managed_object=ro.id, name=rin).first()
            if not ri:
                continue
            result += [{
                "id": str(link.id),
                "local_interface": str(li.id),
                "local_interface__label": li.name,
                "remote_object": ro.id,
                "remote_object__label": ro.name,
                "remote_interface": str(ri.id),
                "remote_interface__label": ri.name,
                "discovery_method": link.method,
                "commited": False,
                "local_description": li.description,
                "remote_description": ri.description
            }]
        return result

    @view(url="^link/approve/$", method=["POST"],
          access="change_link", api=True)
    def api_link_approve(self, request):
        d = json_decode(request.raw_post_data)
        plc = self.get_object_or_404(PendingLinkCheck, id=d.get("link"))
        li = Interface.objects.filter(
            managed_object=plc.local_object.id,
            name=plc.local_interface
            ).first()
        if not li:
            return {
                "success": False,
                "error": "Interface not found: %s:%s" % (
                    plc.local_object.name, plc.local_interface)
            }
        ri = Interface.objects.filter(
            managed_object=plc.remote_object.id,
            name=plc.remote_interface
            ).first()
        if not ri:
            return {
                "success": False,
                "error": "Interface not found: %s:%s" % (
                    plc.remote_object.name, plc.remote_interface)
            }
        li.link_ptp(ri, method=plc.method + "+manual")
        plc.delete()
        return {
            "success": True
        }

    @view(url="^link/reject/$", method=["POST"],
          access="change_link", api=True)
    def api_link_reject(self, request):
        d = json_decode(request.raw_post_data)
        plc = self.get_object_or_404(PendingLinkCheck, id=d.get("link"))
        plc.delete()
        return {
            "success": True
        }

    def check_mrt_access(self, request, name):
        # @todo: Check object's access
        return super(ManagedObjectApplication, self).check_mrt_access(request, name)

    @view(url="^(?P<id>\d+)/discovery/$", method=["GET"],
          access="read", api=True)
    def api_discovery(self, request, id):
        o = self.get_object_or_404(ManagedObject, id=id)
        if not o.has_access(request.user):
            return self.response_forbidden("Access denied")
        link_count = defaultdict(int)
        for link in Link.object_links(o):
            m = link.discovery_method or ""
            if "+" in m:
                m = m.split("+")[0]
            link_count[m] += 1
        r = [{
            "name": "ping",
            "enable_profile": o.object_profile.enable_ping,
            "status": o.get_status(),
            "last_run": None,
            "last_status": None,
            "next_run": None,
            "link_count": None
        }]
        for name in get_active_discovery_methods():
            job = get_job("inv.discovery", name, o.id) or {}
            if name.endswith("_discovery"):
                lcmethod = name[:-10]
            else:
                lcmethod = None
            d = {
                "name": name,
                "enable_profile": getattr(o.object_profile,
                                          "enable_%s" % name),
                "status": job.get("s"),
                "last_run": self.to_json(job.get("last")),
                "last_status": job.get("ls"),
                "next_run": self.to_json(job.get("ts")),
                "link_count": link_count.get(lcmethod, "")
            }
            r += [d]
        return r


    @view(url="^actions/set_managed/$", method=["POST"],
          access="create", api=True,
          validate={
              "ids": ListOfParameter(element=ModelParameter(ManagedObject), convert=True)
          })
    def api_action_set_managed(self, request, ids):
        for o in ids:
            if not o.has_access(request.user):
                continue
            o.is_managed = True
            o.save()
        return "Selected objects set to managed state"

    @view(url="^actions/set_unmanaged/$", method=["POST"],
          access="create", api=True,
          validate={
              "ids": ListOfParameter(element=ModelParameter(ManagedObject), convert=True)
          })
    def api_action_set_unmanaged(self, request, ids):
        for o in ids:
            if not o.has_access(request.user):
                continue
            o.is_managed = False
            o.save()
        return "Selected objects set to unmanaged state"

    @view(url="^(?P<id>\d+)/discovery/run/$", method=["POST"],
          access="change_discovery", api=True)
    def api_run_discovery(self, request, id):
        o = self.get_object_or_404(ManagedObject, id=id)
        if not o.has_access(request.user):
            return self.response_forbidden("Access denied")
        r = json_decode(request.raw_post_data).get("names", [])
        d = 0
        for name in get_active_discovery_methods():
            cfg = "enable_%s" % name
            if getattr(o.object_profile, cfg) and name in r:
                start_schedule("inv.discovery", name, o.id)
                refresh_schedule("inv.discovery",
                                 name, o.id, delta=d)
                d += 1
        return {
            "success": True
        }

    @view(url="^(?P<id>\d+)/discovery/stop/$", method=["POST"],
          access="change_discovery", api=True)
    def api_stop_discovery(self, request, id):
        o = self.get_object_or_404(ManagedObject, id=id)
        if not o.has_access(request.user):
            return self.response_forbidden("Access denied")
        r = json_decode(request.raw_post_data).get("names", [])
        d = 0
        for name in get_active_discovery_methods():
            cfg = "enable_%s" % name
            if getattr(o.object_profile, cfg) and name in r:
                stop_schedule("inv.discovery", name, o.id)
                d += 1
        return {
            "success": True
        }

    @view(url="^(?P<id>\d+)/interface/$", method=["GET"],
        access="read", api=True)
    def api_interface(self, request, id):
        """
        GET interfaces
        :param managed_object:
        :return:
        """
        def sorted_iname(s):
            return sorted(s, key=lambda x: split_alnum(x["name"]))

        def get_style(i):
            profile = i.profile
            if profile:
                try:
                    return style_cache[profile.id]
                except KeyError:
                    pass
                if profile.style:
                    s = profile.style.css_class_name
                else:
                    s = ""
                style_cache[profile.id] = s
                return s
            else:
                return ""

        def get_link(i):
            link = i.link
            if not link:
                return None
            if link.is_ptp:
                # ptp
                o = link.other_ptp(i)
                label = "%s:%s" % (o.managed_object.name, o.name)
            elif link.is_lag:
                # unresolved LAG
                o = [ii for ii in link.other(i)
                     if ii.managed_object.id != i.managed_object.id]
                label = "LAG %s: %s" % (o[0].managed_object.name,
                                        ", ".join(ii.name for ii in o))
            else:
                # Broadcast
                label = ", ".join(
                    "%s:%s" % (ii.managed_object.name, ii.name)
                               for ii in link.other(i))
            return {
                "id": str(link.id),
                "label": label
            }

        # Get object
        o = self.get_object_or_404(ManagedObject, id=int(id))
        if not o.has_access(request.user):
            return self.response_forbidden("Permission denied")
        # Physical interfaces
        # @todo: proper ordering
        default_state = ResourceState.get_default()
        style_cache = {}  ## profile_id -> css_style
        l1 = [
            {
                "id": str(i.id),
                "name": i.name,
                "description": i.description,
                "mac": i.mac,
                "ifindex": i.ifindex,
                "lag": (i.aggregated_interface.name
                        if i.aggregated_interface else ""),
                "link": get_link(i),
                "profile": str(i.profile.id) if i.profile else None,
                "profile__label": unicode(i.profile) if i.profile else None,
                "enabled_protocols": i.enabled_protocols,
                "project": i.project.id if i.project else None,
                "project__label": unicode(i.project) if i.project else None,
                "state": i.state.id if i.state else default_state.id,
                "state__label": unicode(i.state if i.state else default_state),
                "vc_domain": i.vc_domain.id if i.vc_domain else None,
                "vc_domain__label": unicode(i.vc_domain) if i.vc_domain else None,
                "row_class": get_style(i)
            } for i in Interface.objects.filter(
                managed_object=o.id, type="physical")
        ]
        # LAG
        lag = [
            {
                "id": str(i.id),
                "name": i.name,
                "description": i.description,
                "profile": str(i.profile.id) if i.profile else None,
                "profile__label": unicode(i.profile) if i.profile else None,
                "members": [j.name for j in Interface.objects.filter(
                    managed_object=o.id, aggregated_interface=i.id)],
                "row_class": get_style(i)
            } for i in
              Interface.objects.filter(managed_object=o.id,
                                       type="aggregated")
        ]
        # L2 interfaces
        l2 = [
            {
                "name": i.name,
                "description": i.description,
                "untagged_vlan": i.untagged_vlan,
                "tagged_vlans": i.tagged_vlans
            } for i in
              SubInterface.objects.filter(managed_object=o.id,
                  enabled_afi="BRIDGE")
        ]
        # L3 interfaces
        q = MQ(enabled_afi="IPv4") | MQ(enabled_afi="IPv6")
        l3 = [
            {
                "name": i.name,
                "description": i.description,
                "ipv4_addresses": i.ipv4_addresses,
                "ipv6_addresses": i.ipv6_addresses,
                "enabled_protocols": i.enabled_protocols,
                "vlan": i.vlan_ids,
                "vrf": i.forwarding_instance.name if i.forwarding_instance else "",
                "mac": i.mac
            } for i in
              SubInterface.objects.filter(managed_object=o.id).filter(q)
        ]
        return {
            "l1": sorted_iname(l1),
            "lag": sorted_iname(lag),
            "l2": sorted_iname(l2),
            "l3": sorted_iname(l3)
        }

    @view(url="^(?P<id>\d+)/interface/$", method=["POST"],
        access="change_interface", api=True)
    def api_set_interface(self, request, id):
        def get_or_none(c, v):
            if not v:
                return None
            return c.objects.get(id=v)
        o = self.get_object_or_404(ManagedObject, id=int(id))
        if not o.has_access(request.user):
            return self.response_forbidden("Access denied")
        d = json_decode(request.raw_post_data)
        if "id" in d:
            i = self.get_object_or_404(Interface, id=d["id"])
            if i.managed_object.id != o.id:
                return self.response_not_found()
            # Set profile
            if "profile" in d:
                p = get_or_none(InterfaceProfile, d["profile"])
                i.profile = p
                if p:
                    i.profile_locked = True
            # Project
            if "project" in d:
                i.project = get_or_none(Project, d["project"])
            # State
            if "state" in d:
                i.state = get_or_none(ResourceState, d["state"])
            # VC Domain
            if "vc_domain" in d:
                i.vc_domain = get_or_none(VCDomain, d["vc_domain"])
            #
            i.save()
        return {
            "success": True
        }

    @view(method=["DELETE"], url="^(?P<id>\d+)/?$", access="delete", api=True)
    def api_delete(self, request, id):
        """
        Override default method
        :param request:
        :param id:
        :return:
        """
        try:
            o = self.queryset(request).get(id=int(id))
        except self.model.DoesNotExist:
            return self.render_json({
                "status": False,
                "message": "Not found"
            }, status=self.NOT_FOUND)
        if not o.has_access(request.user):
            return self.response_forbidden("Access denied")
        # Run sa.wipe_managed_object job instead
        o.name = "wiping-%d" % o.id
        o.is_managed = False
        o.description = "Wiping! Do not touch!"
        o.save()
        submit_job("main.jobs", "sa.wipe_managedobject", key=o.id)
        return HttpResponse(status=self.DELETED)

    @view(url="^actions/run_discovery/$", method=["POST"],
          access="launch", api=True,
          validate={
              "ids": ListOfParameter(element=ModelParameter(ManagedObject), convert=True)
          })
    def api_action_run_discovery(self, request, ids):
        for o in ids:
            if not o.has_access(request.user):
                continue
            d = 0
            for name in get_active_discovery_methods():
                cfg = "enable_%s" % name
                if getattr(o.object_profile, cfg):
                    refresh_schedule(
                        "inv.discovery",
                        name, o.id, delta=d)
                    d += 1
        return "Discovery processes has been scheduled"

    def get_nested_inventory(self, o):
        rev = o.get_data("asset", "revision")
        if rev == "None":
            rev = ""
        r = {
            "id": str(o.id),
            "serial": o.get_data("asset", "serial"),
            "revision": rev or "",
            "description": o.model.description,
            "model": o.model.name
        }
        children = []
        for n in o.model.connections:
            if n.direction == "i":
                c, r_object, _ = o.get_p2p_connection(n.name)
                if c is None:
                    children += [{
                        "id": None,
                        "name": n.name,
                        "leaf": True,
                        "serial": None,
                        "description": "--- EMPTY ---",
                        "model": None
                    }]
                else:
                    cc = self.get_nested_inventory(r_object)
                    cc["name"] = n.name
                    children += [cc]
            elif n.direction == "s":
                children += [{
                    "id": None,
                    "name": n.name,
                    "leaf": True,
                    "serial": None,
                    "description": n.description,
                    "model": ", ".join(n.protocols)
                }]
        if children:
            to_expand = "Transceiver" not in o.model.name
            r["children"] = children
            r["expanded"] = to_expand
        else:
            r["leaf"] = True
        return r

    @view(url="^(?P<id>\d+)/inventory/$", method=["GET"],
        access="read", api=True)
    def api_inventory(self, request, id):
        o = self.get_object_or_404(ManagedObject, id=id)
        if not o.has_access(request.user):
            return self.response_forbidden("Access denied")
        r = []
        for p in o.get_inventory():
            c = self.get_nested_inventory(p)
            c["name"] = p.name or o.name
            r += [c]
        return {
            "expanded": True,
            "children": r
        }

    @view(url="^(?P<id>\d+)/job_log/(?P<job>[a-zA-Z0-9_]+)/$", method=["GET"],
        access="read", api=True)
    def api_job_log(self, request, id, job):
        o = self.get_object_or_404(ManagedObject, id=id)
        if not o.has_access(request.user):
            return self.response_forbidden("Access denied")
        if not hasattr(self, "discovery_log_jobs"):
            # Read config
            self.discovery_log_jobs = None
            config = SafeConfigParser()
            config.read("etc/noc-discovery.conf")
            if config.has_section("main") and config.has_option("main", "log_jobs"):
                p = config.get("main", "log_jobs")
                if os.path.isdir(p):
                    self.discovery_log_jobs = p
        if self.discovery_log_jobs:
            p = os.path.join(self.discovery_log_jobs, job, id)
            if os.path.exists(p):
                with open(p) as f:
                    return self.render_plain_text(f.read())
        return self.render_plain_text("No data!")

    @view(url="^(?P<id>\d+)/interactions/$", method=["GET"],
          access="interactions", api=True)
    def api_interactions(self, request, id):
        o = self.get_object_or_404(ManagedObject, id=id)
        if not o.has_access(request.user):
            return self.response_forbidden("Access denied")
        return [{
            "ts": self.to_json(i.timestamp),
            "op": i.op,
            "user": i.user,
            "text": i.text
        } for i in InteractionLog.objects.filter(object=o.id).order_by("-timestamp")]

    @view(url="^(?P<id>\d+)/scripts/$", method=["GET"], access="script",
          api=True)
    def api_scripts(self, request, id):
        o = self.get_object_or_404(ManagedObject, id=id)
        if not o.has_access(request.user):
            return self.response_forbidden("Access denied")
        r = []
        for s in sorted(o.profile.scripts):
            script = o.profile.scripts[s]
            interface = script.implements[0]
            ss = {
                "name": s,
                "has_input": any(interface.gen_parameters()),
                "require_input": interface.has_required_params,
                "form": interface.get_form(),
                "preview": interface.preview or "NOC.sa.managedobject.scripts.JSONPreview"
            }
            r += [ss]
        return r

    @view(url="^(?P<id>\d+)/scripts/(?P<name>[^/]+)/$",
          method=["POST"], access="script", api=True)
    def api_run_script(self, request, id, name):
        o = self.get_object_or_404(ManagedObject, id=id)
        if not o.has_access(request.user):
            return self.response_forbidden("Access denied")
        if name not in o.profile.scripts:
            return self.response_not_found("Script not found: %s" % name)
        task = ReduceTask.create_task(
            o, "pyrule:mrt_result", {},
            name, {},
            None)
        return task.id

    @view(url="^(?P<id>\d+)/scripts/(?P<name>[^/]+)/(?P<task>\d+)/$",
          method=["GET"], access="script", api=True)
    def api_get_script_result(self, request, id, name, task):
        o = self.get_object_or_404(ManagedObject, id=id)
        if not o.has_access(request.user):
            return self.response_forbidden("Access denied")
        if name not in o.profile.scripts:
            return self.response_not_found("Script not found: %s" % name)
        t = self.get_object_or_404(ReduceTask, id=int(task))
        try:
            r = t.get_result(block=False)
        except ReduceTask.NotReady:
            # Not ready
            return {
                "ready": False,
                "max_timeout": (t.stop_time - datetime.datetime.now()).seconds,
                "result": None
            }
        # Return result
        return {
            "ready": True,
            "max_timeout": 0,
            "result": r[0]
        }

    @view(url="(?P<id>\d+)/caps/$", method=["GET"],
          access="read", api=True)
    def api_get_caps(self, request, id):
        o = self.get_object_or_404(ManagedObject, id=id)
        if not o.has_access(request.user):
            return self.response_forbidden("Access denied")
        r = []
        oc = ObjectCapabilities.objects.filter(object=o).first()
        if oc:
            for c in oc.caps:
                r += [{
                    "capability": c.capability.name,
                    "description": c.capability.description,
                    "type": c.capability.type,
                    "discovered_value": c.discovered_value,
                    "local_value": c.local_value,
                    "value": c.local_value if c.local_value is not None else c.discovered_value
                }]
        return sorted(r, key=lambda x: x["capability"])

    @view(url="(?P<id>\d+)/facts/$", method=["GET"],
          access="read", api=True)
    def api_get_facts(self, request, id):
        o = self.get_object_or_404(ManagedObject, id=id)
        if not o.has_access(request.user):
            return self.response_forbidden("Access denied")
        return sorted(
            (
                {
                    "cls": f.cls,
                    "label": f.label,
                    "attrs": [
                        {
                            "name": a,
                            "value": f.attrs[a]
                        } for a in f.attrs
                    ],
                    "introduced": f.introduced.isoformat(),
                    "changed": f.changed.isoformat()
                } for f in ObjectFact.objects.filter(object=o.id)),
            key=lambda x: (x["cls"], x["label"]))

    @view(url="(?P<id>\d+)/revalidate/$", method=["POST"],
          access="read", api=True)
    def api_revalidate(self, request, id):
        def revalidate(o):
            engine = Engine(o)
            engine.check()
            return self.response({"status": True}, self.OK)

        o = self.get_object_or_404(ManagedObject, id=id)
        if not o.has_access(request.user):
            return self.response_forbidden("Access denied")
        return self.submit_slow_op(request, revalidate, o)

    @view(url="(?P<id>\d+)/actions/(?P<action>\S+)/$", method=["POST"],
          access="action", api=True)
    def api_action(self, request, id, action):
        def execute(o, a, args):
            return a.execute(o, **args)

        o = self.get_object_or_404(ManagedObject, id=id)
        if not o.has_access(request.user):
            return self.response_forbidden("Access denied")
        a = self.get_object_or_404(Action, name=action)
        # @todo: Check access
        body = request.raw_post_data
        if body:
            args = json_decode(body)
        else:
            args = {}
        return self.submit_slow_op(request, execute, o, a, args)
Esempio n. 27
0
class MapApplication(ExtApplication):
    """
    inv.net application
    """

    title = _("Network Map")
    menu = _("Network Map")
    glyph = "globe"

    implied_permissions = {"launch": ["inv:networksegment:lookup"]}

    # Object statuses
    ST_UNKNOWN = 0  # Object state is unknown
    ST_OK = 1  # Object is OK
    ST_ALARM = 2  # Object is reachable, Active alarms
    ST_UNREACH = 3  # Object is unreachable due to other's object failure
    ST_DOWN = 4  # Object is down
    ST_MAINTENANCE = 32  # Maintenance bit

    @view(r"^(?P<id>[0-9a-f]{24})/data/$",
          method=["GET"],
          access="read",
          api=True)
    def api_data(self, request, id):
        def q_mo(d):
            x = d.copy()
            if x["type"] == "managedobject":
                del x["mo"]
                x["external"] = x["id"] not in mos if is_view else x.get(
                    "role") != "segment"
            elif d["type"] == "cloud":
                del x["link"]
                x["external"] = False
            return x

        # Find segment
        segment = self.get_object_or_404(NetworkSegment, id=id)
        if segment.managed_objects.count() > segment.max_objects:
            # Too many objects
            return {
                "id": str(segment.id),
                "name": segment.name,
                "error": _("Too many objects")
            }
        # if we set selector in segment
        is_view = segment.selector
        if is_view:
            mos = segment.selector.managed_objects.values_list("id", flat=True)
        # Load settings
        settings = MapSettings.objects.filter(segment=id).first()
        node_hints = {}
        link_hints = {}
        if settings:
            self.logger.info("Using stored positions")
            for n in settings.nodes:
                node_hints[n.id] = {
                    "type": n.type,
                    "id": n.id,
                    "x": n.x,
                    "y": n.y
                }
            for ll in settings.links:
                link_hints[ll.id] = {
                    "connector":
                    ll.connector if len(ll.vertices) else "normal",
                    "vertices": [{
                        "x": v.x,
                        "y": v.y
                    } for v in ll.vertices],
                }
        else:
            self.logger.info("Generating positions")
        # Generate topology
        topology = SegmentTopology(
            segment,
            node_hints,
            link_hints,
            force_spring=request.GET.get("force") == "spring")
        topology.layout()
        # Build output
        r = {
            "id": str(segment.id),
            "max_links": int(segment.max_shown_downlinks),
            "name": segment.name,
            "caps": list(topology.caps),
            "nodes": [q_mo(x) for x in topology.G.nodes.values()],
            "links": [topology.G[u][v] for u, v in topology.G.edges()],
        }
        # Parent info
        if segment.parent:
            r["parent"] = {
                "id": str(segment.parent.id),
                "name": segment.parent.name
            }
        # Save settings
        if not settings:
            self.logger.debug("Saving first-time layout")
            MapSettings.load_json({
                "id":
                str(segment.id),
                "nodes":
                [{
                    "type": n["type"],
                    "id": n["id"],
                    "x": n["x"],
                    "y": n["y"]
                } for n in r["nodes"]
                 if n.get("x") is not None and n.get("y") is not None],
                "links": [{
                    "type": n["type"],
                    "id": n["id"],
                    "vertices": n.get("vertices", []),
                    "connector": n.get("connector", "normal"),
                } for n in r["links"]],
            })
        return r

    @view(r"^(?P<id>[0-9a-f]{24})/data/$",
          method=["POST"],
          access="write",
          api=True)
    def api_save(self, request, id):
        self.get_object_or_404(NetworkSegment, id=id)
        data = self.deserialize(request.body)
        data["id"] = id
        MapSettings.load_json(data, request.user.username)
        return {"status": True}

    @view(url=r"^(?P<id>[0-9a-f]{24})/info/segment/$",
          method=["GET"],
          access="read",
          api=True)
    def api_info_segment(self, request, id):
        segment = self.get_object_or_404(NetworkSegment, id=id)
        r = {
            "name": segment.name,
            "description": segment.description,
            "objects": segment.managed_objects.count(),
        }
        return r

    @view(
        url=r"^(?P<id>[0-9a-f]{24})/info/managedobject/(?P<mo_id>\d+)/$",
        method=["GET"],
        access="read",
        api=True,
    )
    def api_info_managedobject(self, request, id, mo_id):
        segment = self.get_object_or_404(NetworkSegment, id=id)
        object = self.get_object_or_404(ManagedObject, id=int(mo_id))
        s = {1: "telnet", 2: "ssh", 3: "http", 4: "https"}[object.scheme]
        r = {
            "id": object.id,
            "name": object.name,
            "description": object.description,
            "address": object.address,
            "platform": object.platform.full_name if object.platform else "",
            "profile": object.profile.name,
            "external": object.segment.id != segment.id,
            "external_segment": {
                "id": str(object.segment.id),
                "name": object.segment.name
            },
            "caps": object.get_caps(),
            "console_url": "%s://%s/" % (s, object.address),
        }
        return r

    @view(
        url=r"^(?P<id>[0-9a-f]{24})/info/link/(?P<link_id>[0-9a-f]{24})/$",
        method=["GET"],
        access="read",
        api=True,
    )
    def api_info_link(self, request, id, link_id):
        def q(s):
            if isinstance(s, str):
                s = s.encode("utf-8")
            return s

        self.get_object_or_404(NetworkSegment, id=id)
        link = self.get_object_or_404(Link, id=link_id)
        r = {
            "id": str(link.id),
            "name": link.name or None,
            "description": link.description or None,
            "objects": [],
            "method": link.discovery_method,
        }
        o = defaultdict(list)
        for i in link.interfaces:
            o[i.managed_object] += [i]
        for mo in sorted(o, key=lambda x: x.name):
            r["objects"] += [{
                "id":
                mo.id,
                "name":
                mo.name,
                "interfaces": [{
                    "name": i.name,
                    "description": i.description or None,
                    "status": i.status
                } for i in sorted(o[mo], key=lambda x: alnum_key(x.name))],
            }]
        # Get link bandwidth
        mo_in = defaultdict(float)
        mo_out = defaultdict(float)
        mos = [ManagedObject.get_by_id(mo["id"]) for mo in r["objects"]]
        metric_map, last_ts = get_interface_metrics(list(o))
        for mo in o:
            if mo not in metric_map:
                continue
            for i in o[mo]:
                if i.name not in metric_map[mo]:
                    continue
                mo_in[mo] += metric_map[mo][i.name]["Interface | Load | In"]
                mo_out[mo] += metric_map[mo][i.name]["Interface | Load | Out"]
        if len(mos) == 2:
            mo1, mo2 = mos
            r["utilisation"] = [
                int(max(mo_in[mo1], mo_out[mo2])),
                int(max(mo_in[mo2], mo_out[mo1])),
            ]
        else:
            mv = list(mo_in.values()) + list(mo_out.values())
            if mv:
                r["utilisation"] = [int(max(mv))]
            else:
                r["utilisation"] = 0
        return r

    @view(
        url=r"^(?P<id>[0-9a-f]{24})/info/cloud/(?P<link_id>[0-9a-f]{24})/$",
        method=["GET"],
        access="read",
        api=True,
    )
    def api_info_cloud(self, request, id, link_id):
        self.get_object_or_404(NetworkSegment, id=id)
        link = self.get_object_or_404(Link, id=link_id)
        r = {
            "id": str(link.id),
            "name": link.name or None,
            "description": link.description or None,
            "objects": [],
            "method": link.discovery_method,
        }
        o = defaultdict(list)
        for i in link.interfaces:
            o[i.managed_object] += [i]
        for mo in sorted(o, key=lambda x: x.name):
            r["objects"] += [{
                "id":
                mo.id,
                "name":
                mo.name,
                "interfaces": [{
                    "name": i.name,
                    "description": i.description or None,
                    "status": i.status
                } for i in sorted(o[mo], key=lambda x: alnum_key(x.name))],
            }]
        return r

    @view(
        url=r"^objects_statuses/$",
        method=["POST"],
        access="read",
        api=True,
        validate={"objects": ListOfParameter(IntParameter())},
    )
    def api_objects_statuses(self, request, objects: List[int]):
        def get_alarms(objects: List[int]) -> Set[int]:
            """
            Returns a set of objects with alarms
            """
            alarms: Set[int] = set()
            coll = ActiveAlarm._get_collection()
            while objects:
                chunk, objects = objects[:500], objects[500:]
                a = coll.aggregate([
                    {
                        "$match": {
                            "managed_object": {
                                "$in": chunk
                            }
                        }
                    },
                    {
                        "$group": {
                            "_id": "$managed_object",
                            "count": {
                                "$sum": 1
                            }
                        }
                    },
                ])
                alarms.update(d["_id"] for d in a)
            return alarms

        def get_maintenance(objects: List[int]) -> Set[int]:
            """
            Returns a set of objects currently in maintenance
            :param objects:
            :return:
            """
            now = datetime.datetime.now()
            so = set(objects)
            mnt_objects = set()
            pipeline = [
                {
                    "$match": {
                        "affected_objects.object": {
                            "$in": list(so)
                        }
                    }
                },
                {
                    "$unwind": "$affected_objects"
                },
                {
                    "$lookup": {
                        "from":
                        "noc.maintenance",
                        "as":
                        "m",
                        "let": {
                            "maintenance": "_id"
                        },
                        "pipeline": [{
                            "$match": {
                                "m.is_completed": False,
                                "m.start": {
                                    "$lte": now
                                },
                                "m.stop": {
                                    "gte": now
                                },
                            },
                        }],
                    },
                },
                {
                    "$project": {
                        "_id": 0,
                        "object": "$affected_objects.object",
                    }
                },
                {
                    "$group": {
                        "_id": "$object"
                    }
                },
            ]
            mnt_objects |= so & {
                x["_id"]
                for x in AffectedObjects._get_collection().aggregate(pipeline)
            }
            return mnt_objects

        # Mark all as unknown
        r = {o: self.ST_UNKNOWN for o in objects}
        sr = ObjectStatus.get_statuses(objects)
        sa = get_alarms(objects)
        mo = get_maintenance(objects)
        for o in sr:
            if sr[o]:
                # Check for alarms
                if o in sa:
                    r[o] = self.ST_ALARM
                else:
                    r[o] = self.ST_OK
            else:
                r[o] = self.ST_DOWN
            if o in mo:
                r[o] |= self.ST_MAINTENANCE
        return r

    @classmethod
    @cachedmethod(key="managedobject-name-to-id-%s", lock=lambda _: tags_lock)
    def managedobject_name_to_id(cls, name):
        r = ManagedObject.objects.filter(name=name).values_list("id")
        if r:
            return r[0][0]
        return None

    @classmethod
    @cachedmethod(key="interface-tags-to-id-%s-%s", lock=lambda _: tags_lock)
    def interface_tags_to_id(cls, object_name, interface_name):
        mo = cls.managedobject_name_to_id(object_name)
        i = Interface._get_collection().find_one({
            "managed_object": mo,
            "name": interface_name
        })
        if i:
            return i["_id"]
        return None

    @view(
        url=r"^metrics/$",
        method=["POST"],
        access="read",
        api=True,
        validate={
            "metrics":
            DictListParameter(
                attrs={
                    "id": StringParameter(),
                    "metric": StringParameter(),
                    "tags": DictParameter(),
                })
        },
    )
    def api_metrics(self, request, metrics):
        def q(s):
            if isinstance(s, str):
                s = s.encode("utf-8")
            return s

        def qt(t):
            return "|".join(["%s=%s" % (v, t[v]) for v in sorted(t)])

        # Build query
        tag_id = {}  # object, interface -> id
        if_ids = {}  # id -> port id
        mlst = []  # (metric, object, interface)
        for m in metrics:
            if "object" in m["tags"] and "interface" in m["tags"]:
                if not m["tags"]["object"]:
                    continue
                try:
                    if_ids[self.interface_tags_to_id(
                        m["tags"]["object"], m["tags"]["interface"])] = m["id"]
                    object = ManagedObject.objects.get(
                        name=m["tags"]["object"])
                    tag_id[object, m["tags"]["interface"]] = m["id"]
                    mlst += [(m["metric"], object, m["tags"]["interface"])]
                except KeyError:
                    pass
        # @todo: Get last values from cache
        if not mlst:
            return {}

        r = {}
        # Apply interface statuses
        for d in Interface._get_collection().find(
            {"_id": {
                "$in": list(if_ids)
            }}, {
                "_id": 1,
                "admin_status": 1,
                "oper_status": 1
            }):
            r[if_ids[d["_id"]]] = {
                "admin_status": d.get("admin_status", True),
                "oper_status": d.get("oper_status", True),
            }
        metric_map, last_ts = get_interface_metrics([m[1] for m in mlst])
        # Apply metrics
        for rq_mo, rq_iface in tag_id:
            pid = tag_id.get((rq_mo, rq_iface))
            if not pid:
                continue
            if pid not in r:
                r[pid] = {}
            if rq_mo not in metric_map:
                continue
            if rq_iface not in metric_map[rq_mo]:
                continue
            r[pid]["Interface | Load | In"] = metric_map[rq_mo][rq_iface][
                "Interface | Load | In"]
            r[pid]["Interface | Load | Out"] = metric_map[rq_mo][rq_iface][
                "Interface | Load | Out"]

        return r

    @view(r"^(?P<id>[0-9a-f]{24})/data/$",
          method=["DELETE"],
          access="write",
          api=True)
    def api_reset(self, request, id):
        self.get_object_or_404(NetworkSegment, id=id)
        MapSettings.objects.filter(segment=id).delete()
        return {"status": True}

    @view(
        url=r"^stp/status/$",
        method=["POST"],
        access="read",
        api=True,
        validate={"objects": ListOfParameter(IntParameter())},
    )
    def api_objects_stp_status(self, request, objects):
        def get_stp_status(object_id):
            roots = set()
            blocked = set()
            object = ManagedObject.get_by_id(object_id)
            sr = object.scripts.get_spanning_tree()
            for instance in sr["instances"]:
                ro = DiscoveryID.find_object(instance["root_id"])
                if ro:
                    roots.add(ro)
                for i in instance["interfaces"]:
                    if i["state"] == "discarding" and i["role"] == "alternate":
                        iface = object.get_interface(i["interface"])
                        if iface:
                            link = iface.link
                            if link:
                                blocked.add(str(link.id))
            return object_id, roots, blocked

        r = {"roots": [], "blocked": []}
        futures = []
        with ThreadPoolExecutor(max_workers=10) as executor:
            for o in objects:
                futures += [executor.submit(get_stp_status, o)]
            for future in as_completed(futures):
                try:
                    obj, roots, blocked = future.result()
                    for ro in roots:
                        if ro.id not in r["roots"]:
                            r["roots"] += [ro.id]
                    r["blocked"] += blocked
                except Exception as e:
                    self.logger.error("[stp] Exception: %s", e)
        return r
Esempio n. 28
0
File: views.py Progetto: nbashev/noc
class ObjectListApplication(ExtApplication):
    """
    ManagedObject application
    """

    model = ManagedObject
    # Default filter by is_managed
    managed_filter = True

    def queryset(self, request, query=None):
        """
        Filter records for lookup
        """
        self.logger.info("Queryset %s" % query)
        if self.managed_filter:
            q = d_Q(is_managed=True)
        else:
            q = d_Q()
        if not request.user.is_superuser:
            q &= UserAccess.Q(request.user)
        if query:
            sq = ManagedObject.get_search_Q(query)
            if sq:
                q &= sq
            else:
                q &= d_Q(name__contains=query)
        return self.model.objects.filter(q)

    def instance_to_dict(self, o, fields=None):
        return {
            "id":
            str(o.id),
            "name":
            o.name,
            "address":
            o.address,
            "profile_name":
            o.profile.name,
            "platform":
            o.platform.name if o.platform else "",
            "version":
            o.version.version if o.version else "",
            "row_class":
            o.object_profile.style.css_class_name
            if o.object_profile.style else ""
            # "row_class": ""
        }

    def cleaned_query(self, q):
        nq = {}
        for k in q:
            if not k.startswith("_") and "__" not in k:
                nq[k] = q[k]
        ids = set()
        self.logger.debug("Cleaned query: %s" % nq)
        if "ids" in nq:
            ids = {int(nid) for nid in nq["ids"]}
            del nq["ids"]

        if "administrative_domain" in nq:
            ad = AdministrativeDomain.get_nested_ids(
                int(nq["administrative_domain"]))
            if ad:
                del nq["administrative_domain"]
                nq["administrative_domain__in"] = ad

        if "selector" in nq:
            s = self.get_object_or_404(ManagedObjectSelector,
                                       id=int(q["selector"]))
            if s:
                if ids:
                    # nq["id__in"] = set(ManagedObject.objects.filter(s.Q).values_list("id", flat=True))
                    ids = ids.intersection(
                        set(
                            ManagedObject.objects.filter(s.Q).values_list(
                                "id", flat=True)))
                else:
                    ids = set(
                        ManagedObject.objects.filter(s.Q).values_list(
                            "id", flat=True))
            del nq["selector"]
        mq = None
        c_in = []
        c_nin = []
        for cc in [part for part in nq if part.startswith("caps")]:
            """
            Caps: caps0=CapsID,caps1=CapsID:true....
            cq - caps query
            mq - main_query
            caps0=CapsID - caps is exists
            caps0=!CapsID - caps is not exists
            caps0=CapsID:true - caps value equal True
            caps0=CapsID:2~50 - caps value many then 2 and less then 50
            c_ids = set(ObjectCapabilities.objects(cq).distinct('object'))
            """
            # @todo Убирать дубликаты (повторно не добавлять)

            c = nq.pop(cc)
            if not c:
                continue
            if not mq:
                mq = m_Q()
            self.logger.info("Caps: %s" % c)
            if "!" in c:
                # @todo Добавить исключение (только этот) !ID
                c_id = c[1:]
                c_query = "nexists"
            elif ":" not in c:
                c_id = c
                c_query = "exists"
            else:
                c_id, c_query = c.split(":", 1)

            try:
                c_id = bson.ObjectId(c_id)
            except bson.errors.InvalidId as e:
                self.logger.warning(e)
                continue
            if "~" in c_query:
                l, r = c_query.split("~")
                if not l:
                    cond = {"$lte": int(r)}
                elif not r:
                    cond = {"$gte": int(l)}
                else:
                    cond = {"$lte": int(r), "$gte": int(l)}
                cq = m_Q(__raw__={
                    "caps": {
                        "$elemMatch": {
                            "capability": c_id,
                            "value": cond
                        }
                    }
                })
            elif c_query in ("false", "true"):
                cq = m_Q(caps__match={
                    "capability": c_id,
                    "value": c_query == "true"
                })
            elif c_query == "exists":
                c_in += [c_id]
                continue
            elif c_query == "nexists":
                c_nin += [c_id]
                continue
            else:
                try:
                    c_query = int(c_query)
                    cq = m_Q(
                        __raw__={
                            "caps": {
                                "$elemMatch": {
                                    "capability": c_id,
                                    "value": int(c_query)
                                }
                            }
                        })
                except ValueError:
                    cq = m_Q(
                        __raw__={
                            "caps": {
                                "$elemMatch": {
                                    "capability": c_id,
                                    "value": {
                                        "$regex": c_query
                                    }
                                }
                            }
                        })
            mq &= cq
        if c_in:
            mq &= m_Q(caps__capability__in=c_in)
        if c_nin:
            mq &= m_Q(caps__capability__nin=c_nin)
        if mq:
            c_ids = set(el["_id"] for el in ObjectCapabilities.objects(
                mq).values_list("object").as_pymongo())
            self.logger.info("Caps objects count: %d" % len(c_ids))
            ids = ids.intersection(c_ids) if ids else c_ids

        if "addresses" in nq:
            if isinstance(nq["addresses"], list):
                nq["address__in"] = nq["addresses"]
            else:
                nq["address__in"] = [nq["addresses"]]
            del nq["addresses"]
        if ids:
            nq["id__in"] = list(ids)

        xf = list((set(nq.keys())) -
                  set(f.name for f in self.model._meta.get_fields()))
        # @todo move validation fields
        for x in xf:
            if x in ["address__in", "id__in", "administrative_domain__in"]:
                continue
            self.logger.warning("Remove element not in model: %s" % x)
            del nq[x]
        return nq

    def extra_query(self, q, order):
        extra = {"select": {}}
        if "address" in order:
            extra["select"]["ex_address"] = " cast_test_to_inet(address) "
            extra["order_by"] = ["ex_address", "address"]
        elif "-address" in order:
            extra["select"]["ex_address"] = " cast_test_to_inet(address) "
            extra["order_by"] = ["-ex_address", "-address"]

        self.logger.info("Extra: %s" % extra)
        return extra, [] if "order_by" in extra else order

    @view(method=["GET", "POST"], url="^$", access="read", api=True)
    def api_list(self, request):
        return self.list_data(request, self.instance_to_dict)

    @view(
        method=["POST"],
        url="^iplist/$",
        access="launch",
        api=True,
        validate={
            "query":
            DictParameter(
                attrs={
                    "addresses":
                    ListOfParameter(element=IPv4Parameter(), convert=True)
                })
        },
    )
    def api_action_ip_list(self, request, query):
        # @todo Required user vault implementation
        return self.render_json({"status": True})
Esempio n. 29
0
class RenderApplication(ExtApplication):
    """
    Graphite-compatible render
    """
    title = "Render"

    DEFAULT_GRAPH_WIDTH = 330
    DEFAULT_GRAPH_HEIGTH = 250

    # Empty space around the borders of chart
    X_PADDING = 10
    Y_PADDING = 10
    #

    @view(url="^$", method=["GET"], access="launch",
          validate={
              "graphType": StringParameter(
                  default="line",
                  choices=GraphTypes.keys()
              ),
              "pieMode": StringParameter(
                  default="average",
                  # @todo: Specify all modes
                  choices=["average"]
              ),
              "cacheTimeout": IntParameter(
                  min_value=0,
                  default=config.getint("pm_render", "cache_duration")
              ),
              "target": ListOfParameter(
                  element=StringParameter(),
                  convert=True, default=[]
              ),
              "localOnly": StringParameter(default="0"),
              "tz": StringParameter(default=TIME_ZONE),
              "pickle": StringParameter(required=False),
              "rawData": StringParameter(required=False),
              "jsonp": StringParameter(required=False),
              "format": StringParameter(required=False),
              "noCache": StringParameter(required=False),
              "maxDataPoints": IntParameter(required=False)
          },
          api=True)
    def api_render(self, request,
                   graphType=None, pieMode=None, cacheTimeout=None,
                   target=None, localOnly=None, tz=None, pickle=None,
                   rawData=None, jsonp=None,
                   noCache=None, format=None,
                   maxDataPoints=None,
                   **kwargs):
        # Get timezone info
        try:
            tz = pytz.timezone(tz)
        except pytz.UnknownTimeZoneError:
            tz = pytz.timezone(TIME_ZONE)
        # Get format
        if pickle is not None:
            format = "pickle"
        elif rawData is not None:
            format = "raw"
        # Get time range
        try:
            t0 = parseATTime(kwargs.get("from", "-1d"))
            t1 = parseATTime(kwargs.get("until", "now"))
        except Exception, why:
            return self.response_bad_request(
                "Cannot parse time: %s" % why
            )
        if t0 == t1:
            return self.response_bad_request("Empty time range")
        # Collect parameters
        request_opts = {
            "graphType": graphType,
            "graphClass": GraphTypes[graphType],
            "pieMode": pieMode,
            "targets": target or [],
            "localOnly": localOnly == "1",
            "tzinfo": tz,
            "format": format,
            "noCache": noCache is not None,
            "startTime": min(t0, t1),
            "endTime": max(t0, t1),
            "cacheTimeout": cacheTimeout
        }
        if format:
            request_opts["format"] = format
            if jsonp is not None:
                request_opts["jsonp"] = jsonp
        # Fill possible graph options
        graph_opts = {
            "width": self.DEFAULT_GRAPH_WIDTH,
            "height": self.DEFAULT_GRAPH_HEIGTH,
        }
        if format == "svg":
            graph_opts["outputFormat"] = "svg"
        for opt in request_opts["graphClass"].customizable:
            if opt in kwargs:
                v = kwargs[opt]
                if opt not in ("fgcolor", "bgcolor", "fontColor"):
                    try:
                        graph_opts[opt] = int(v)
                        continue
                    except ValueError:
                        pass
                try:
                    graph_opts[opt] = float(v)
                    continue
                except ValueError:
                    pass
                if v.lower() in ("true", "false"):
                    graph_opts[opt] = v.lower() == "true"
                    continue
                if not v or v.lower() == "default":
                    continue
                graph_opts[opt] = v
        use_cache = not request_opts["noCache"]
        cache_timeout = request_opts["cacheTimeout"]
        ctx = {
            "startTime": request_opts["startTime"],
            "endTime": request_opts["endTime"],
            "localOnly": request_opts["localOnly"],
            "maxDataPoints": maxDataPoints,
            "data": []
        }
        data = ctx["data"]
        # Try to use cached response
        if use_cache:
            request_key = hashRequest(request)
            cached_response = cache.get(request_key)
            if cached_response:
                return cached_response
            else:
                request_opts["requestKey"] = request_key
        # Cache miss, prepare requested data
        if graphType == "pie":
            for t in request_opts["targets"]:
                if ":" in t:
                    try:
                        name, value = t.split(":", 1)
                        data += [(name, float(value))]
                    except ValueError:
                        raise ValueError("Invalid target: '%s'" % t)
                else:
                    for series in evaluateTarget(ctx, t):
                        f = PieFunctions(request_opts["pieMode"])
                        data += [(series.name, f(ctx, series) or 0)]
        elif graphType == "line":
            if use_cache:
                # Store cached data
                data_key = hashData(request_opts["targets"],
                                    request_opts["startTime"],
                                    request_opts["endTime"])
                cached_data = cache.get(data_key)
            else:
                cached_data = None
            if cached_data is None:
                for t in request_opts["targets"]:
                    if not t.strip():
                        continue
                    data.extend(evaluateTarget(ctx, t))
                if use_cache:
                    cache.set(
                        data_key,
                        [d.get_info() for d in data],
                        cache_timeout
                    )
            else:
                # Convert cached data to Time Series
                data = [TimeSeries(**a) for a in cached_data]
        # Return data in requested format
        h = getattr(self, "get_%s_response" % request_opts["format"], None)
        if h:
            r = h(data, request_opts)
        else:
            graph_opts["data"] = data
            r = self.render_graph(request_opts, graph_opts)
        r["Pragma"] = "no-cache"
        r["Cache-Control"] = "no-cache"
        return r