diff --git a/services/web/apps/inv/inv/plugins/crossing.py b/services/web/apps/inv/inv/plugins/crossing.py new file mode 100644 index 0000000000000000000000000000000000000000..37ee37d30c26b6f4442a2103421859efeb526539 --- /dev/null +++ b/services/web/apps/inv/inv/plugins/crossing.py @@ -0,0 +1,56 @@ +# --------------------------------------------------------------------- +# inv.inv crossing plugin +# --------------------------------------------------------------------- +# Copyright (C) 2007-2020 The NOC Project +# See LICENSE for details +# --------------------------------------------------------------------- + +from itertools import chain + +# NOC modules +from .base import InvPlugin +from noc.inv.models.object import Object +from noc.inv.models.objectmodel import ObjectModel +from noc.inv.models.objectconnection import ObjectConnection + + +class CrossingPlugin(InvPlugin): + name = "crossing" + js = "NOC.inv.inv.plugins.crossing.CrossingPanel" + + def get_data(self, request, o): + cables_model = ObjectModel.objects.filter(data__length__length__gte=0) + oc_ids = chain.from_iterable( + ObjectConnection.objects.filter( + __raw__={"connection": {"$elemMatch": {"object": {"$in": o.get_nested_ids()}}}} + ).scalar("connection") + ) + + r = [] + for oo in Object.objects.filter( + model__in=cables_model, id__in=[x.object.id for x in oc_ids] + ): + c1, c2 = None, None + c = ObjectConnection.objects.filter( + __raw__={"connection": {"$elemMatch": {"object": oo.id}}} + )[:2] + if c: + c1 = [c for c in c[0].connection if c.object != oo][0] + if len(c) > 1: + c2 = [c for c in c[1].connection if c.object != oo][0] + r += [ + { + "name": oo.name, + "id": str(oo.id), + "length": oo.get_data("length", "length"), + "model": str(oo.model.id), + "model__label": oo.model.name, + "object_start": str(c1.object.id), + "object_start_slot": c1.name, + "object_start__label": c1.object.name if c1 else "", + "object_end": str(c2.object.id), + "object_end_slot": c2.name, + "object_end__label": c2.object.name if c2 else "", + } + ] + return r diff --git a/services/web/apps/inv/inv/plugins/sensor.py b/services/web/apps/inv/inv/plugins/sensor.py index ec5854423ceec77284d141923cee79d9d0266173..345a80d9ef7cc9d306eb38668d0d327b3bfe206c 100644 --- a/services/web/apps/inv/inv/plugins/sensor.py +++ b/services/web/apps/inv/inv/plugins/sensor.py @@ -17,7 +17,7 @@ class SensorPlugin(InvPlugin): name = "sensor" js = "NOC.inv.inv.plugins.sensor.SensorPanel" - def get_data(self, request, o: "Sensor"): + def get_data(self, request, o): return [ { "profile__label": s.profile.name, @@ -38,7 +38,7 @@ class SensorPlugin(InvPlugin): "bi_id": str(s.bi_id), "id": str(s.id), } - for s in Sensor.objects.filter(object=o.id) + for s in Sensor.objects.filter(object__in=o.get_nested_ids()) ] def init_plugin(self): diff --git a/services/web/apps/inv/inv/views.py b/services/web/apps/inv/inv/views.py index 9bc29d6d041e53d48fb24d27029be40a4f22d202..01c228a0ffe4a9c2e5f89d4b7d00b11cf887b388 100644 --- a/services/web/apps/inv/inv/views.py +++ b/services/web/apps/inv/inv/views.py @@ -8,7 +8,7 @@ # Python modules import inspect import os -from typing import Optional, Dict, List, Any +from typing import Optional, Dict, List, Any, Tuple # NOC modules from noc.lib.app.extapplication import ExtApplication, view @@ -27,6 +27,8 @@ from noc.core.inv.path import find_path from noc.core.translation import ugettext as _ from noc.core.comp import smart_text +translation_map = str.maketrans("<>", "><") + class InvApplication(ExtApplication): """ @@ -62,64 +64,6 @@ class InvApplication(ExtApplication): def get_plugin_data(self, name): return {"name": name, "xtype": self.plugins[name].js} - def get_remote_slot(self, left_slot, lo, ro): - """ - Determing right device's slot with find_path method - :return: - """ - for path in find_path(lo, left_slot.name, left_slot.protocols): - if path.obj == ro: - return path - - def get_remote_device(self, slot, protocols, o): - """ - Determing remote device with find_path method - :return: - """ - for path in find_path(o, slot, protocols): - if path.obj != o and not path.obj.name.startswith("Wire"): - return path - - def get_cs_item( - self, - id, - name, - type, - type__label, - gender, - direction, - protocols, - free, - valid, - disable_reason, - o, - ): - """ - Creating member of cs dict - :return: - """ - cs = { - "id": id, - "name": name, - "type": type, - "type__label": type__label, - "gender": gender, - "direction": direction, - "protocols": protocols, - "free": free, - "valid": valid, - "disable_reason": disable_reason, - } - if not free: - rd = self.get_remote_device(name, protocols, o) - if rd: - cs["remote_device"] = { - "name": rd.obj.name, - "id": smart_text(rd.obj.id), - "slot": rd.connection, - } - return cs - @view("^node/$", method=["GET"], access="read", api=True) def api_node(self, request): children = [] @@ -139,7 +83,7 @@ class InvApplication(ExtApplication): {"data.container.container": True}, {"_id": 1} ) ] - children = [ + children: List[Tuple[str, "Object"]] = [ (o.name, o) for o in Object.objects.filter( __raw__={"container": None, "model": {"$in": cmodels}} @@ -187,6 +131,9 @@ class InvApplication(ExtApplication): self.get_plugin_data("file"), self.get_plugin_data("log"), ] + if o.get_data("container", "container"): + n["plugins"] += [self.get_plugin_data("sensor")] + n["plugins"] += [self.get_plugin_data("crossing")] # Process disabled plugins n["plugins"] = [p for p in n["plugins"] if p["name"] not in disabled_plugins] r += [n] @@ -489,3 +436,66 @@ class InvApplication(ExtApplication): self.logger.warning("Connection Error: %s", str(e)) return self.render_json({"status": False, "text": str(e)}) return True + + def get_remote_slot(self, left_slot, lo, ro): + """ + Determing right device's slot with find_path method + :return: + """ + for path in ( + find_path( + lo, left_slot.name, [p.translate(translation_map) for p in left_slot.protocols] + ) + or [] + ): + if path.obj == ro: + return path + + def get_remote_device(self, slot, protocols, o): + """ + Determing remote device with find_path method + :return: + """ + for path in find_path(o, slot, [p.translate(translation_map) for p in protocols]) or []: + if path.obj != o and not path.obj.get_data("length", "length"): + return path + + def get_cs_item( + self, + id, + name, + type, + type__label, + gender, + direction, + protocols, + free, + valid, + disable_reason, + o, + ): + """ + Creating member of cs dict + :return: + """ + cs = { + "id": id, + "name": name, + "type": type, + "type__label": type__label, + "gender": gender, + "direction": direction, + "protocols": protocols, + "free": free, + "valid": valid, + "disable_reason": disable_reason, + } + if not free: + rd = self.get_remote_device(name, protocols, o) + if rd: + cs["remote_device"] = { + "name": rd.obj.name, + "id": smart_text(rd.obj.id), + "slot": rd.connection, + } + return cs diff --git a/ui/web/inv/inv/CreateConnectionForm.js b/ui/web/inv/inv/CreateConnectionForm.js index 015f11ff6cd719c28341d9cefda7284f021349ab..39d882689152c3b52009513b9b4be8d6ea867de2 100644 --- a/ui/web/inv/inv/CreateConnectionForm.js +++ b/ui/web/inv/inv/CreateConnectionForm.js @@ -243,12 +243,14 @@ Ext.define("NOC.inv.inv.CreateConnectionForm", { if(!port.free) { color = me.OCCUPIED_COLOR; - remoteId = port.remote_device.id; - remoteName = port.remote_device.name; - if(labelAlign === "left") { - name = port.remote_device.name + "/" + port.remote_device.slot + " <= " + port.name; - } else { - name += " => " + port.remote_device.name + "/" + port.remote_device.slot; + if(port.remote_device) { + remoteId = port.remote_device.id; + remoteName = port.remote_device.name; + if (labelAlign === "left") { + name = port.remote_device.name + "/" + port.remote_device.slot + " <= " + port.name; + } else { + name += " => " + port.remote_device.name + "/" + port.remote_device.slot; + } } } if(!port.valid) { diff --git a/ui/web/inv/inv/plugins/crossing/CrossingPanel.js b/ui/web/inv/inv/plugins/crossing/CrossingPanel.js new file mode 100644 index 0000000000000000000000000000000000000000..0ce1098ec53a81f06e98e4d4801c4b98696f7933 --- /dev/null +++ b/ui/web/inv/inv/plugins/crossing/CrossingPanel.js @@ -0,0 +1,79 @@ +//--------------------------------------------------------------------- +// inv.inv SensorPanel +//--------------------------------------------------------------------- +// Copyright (C) 2007-2014 The NOC Project +// See LICENSE for details +//--------------------------------------------------------------------- +console.debug("Defining NOC.inv.inv.plugins.crossing.CrossingPanel"); + +Ext.define("NOC.inv.inv.plugins.crossing.CrossingPanel", { + extend: "Ext.panel.Panel", + title: __("Crossing"), + closable: false, + layout: "fit", + requires: [ + "Ext.ux.form.GridField" + ], + initComponent: function() { + var me = this; + + me.gridField = Ext.create({ + xtype: "gridfield", + save: false, + saveHandler: me.saveHandler, + columns: [ + { + dataIndex: "name", + text: __("Name"), + width: 300 + }, + { + dataIndex: "object_start__label", + text: __("Object Start"), + width: 200 + }, + { + dataIndex: "object_start_slot", + text: __("Object Start Slot"), + width: 70 + }, + { + dataIndex: "object_end__label", + text: __("Object End"), + width: 200 + }, + { + dataIndex: "object_end_slot", + text: __("Object End Slot"), + width: 70 + }, + { + dataIndex: "model__label", + text: __("Model"), + width: 200 + }, + { + dataIndex: "length", + text: __("Length"), + }, + ] + }); + Ext.apply(me, { + items: [ + me.gridField + ] + }); + me.callParent(); + }, + // + preview: function(data) { + var me = this; + me.currentId = data.id; + me.gridField.store.loadData(data) + }, + // + saveHandler: function() { + var me = this; + console.log(me); + } +});