Commit 91f92aa6 authored by Dmitry Volodin's avatar Dmitry Volodin Committed by EKbfh
Browse files

Sensors

parent 7d835201
Pipeline #29279 failed with stages
in 1 minute and 38 seconds
# Sensor Profile
A group settings for [Sensors](../sensor/index.md)
# Sensor
Sensors are the measurement equipment, allocated somewhere and capable
to perform measures of exactly one parameter. Values are measured in
[Measurement Units](../../measurement-units/index.md). Sensors may be
built-in (in-chip thermometers, etc) and external () as well.
## Group Settings
Group settings for `Sensors` are contained in
[Sensor Profile](../sensor-profile/index.md)
......@@ -45,6 +45,8 @@ nav:
- Remote System: user/reference/concepts/remote-system/index.md
- Resource Group: user/reference/concepts/resource-group/index.md
- SA Profile: user/reference/concepts/sa-profile/index.md
- Sensor: user/reference/concepts/sensor/index.md
- Sensor Profile: user/reference/concepts/sensor-profile/index.md
- Service: user/reference/concepts/service/index.md
- Service Profile: user/reference/concepts/service-profile/index.md
- Subscriber: user/reference/concepts/subscriber/index.md
......@@ -1097,16 +1099,16 @@ nav:
- "Service Down": user/reference/event-classes/vendor/f5/bigip/network/load-balance/service-down.md
- "Service Up": user/reference/event-classes/vendor/f5/bigip/network/load-balance/service-up.md
- Measurement Units:
- Overview: user/reference/measurementunits/index.md
- Ampere: user/reference/measurementunits/ampere.md
- Celsius: user/reference/measurementunits/celsius.md
- Hertz: user/reference/measurementunits/hertz.md
- Meter: user/reference/measurementunits/meter.md
- RPM: user/reference/measurementunits/rpm.md
- Second: user/reference/measurementunits/second.md
- Volt AC: user/reference/measurementunits/volt-ac.md
- Volt DC: user/reference/measurementunits/volt-dc.md
- Watt: user/reference/measurementunits/watt.md
- Overview: user/reference/measurement-units/index.md
- Ampere: user/reference/measurement-units/ampere.md
- Celsius: user/reference/measurement-units/celsius.md
- Hertz: user/reference/measurement-units/hertz.md
- Meter: user/reference/measurement-units/meter.md
- RPM: user/reference/measurement-units/rpm.md
- Second: user/reference/measurement-units/second.md
- Volt AC: user/reference/measurement-units/volt-ac.md
- Volt DC: user/reference/measurement-units/volt-dc.md
- Watt: user/reference/measurement-units/watt.md
- Metrics:
- Metric Scopes:
- Overview: user/reference/metrics/scopes/index.md
......
......@@ -235,7 +235,7 @@ class MeasurementUnits(object):
@property
def rel_path(self) -> str:
return f"reference/measurementunits/{self.file_name}"
return f"reference/measurement-units/{self.file_name}"
class CollectionDoc(object):
......@@ -470,8 +470,8 @@ class CollectionDoc(object):
new_files = 0
changed_files = 0
unmodified_files = 0
toc = []
ec_root = os.path.join(self.doc_root, "reference", "event-classes")
toc = ["- Overview: user/reference/event-classes/index.md"]
ec_root = os.path.join(self.doc_root, "user", "reference", "event-classes")
last_path: List[str] = []
indent: str = ""
for ec_name in sorted(self.event_class):
......@@ -495,7 +495,7 @@ class CollectionDoc(object):
last_path = path
short_name = ec_name.split(" | ")[-1].strip()
toc += [
f'{indent} - "{short_name}": reference/event-classes/{rel_dir}/{ec.file_name}'
f'{indent} - "{short_name}": user/reference/event-classes/{rel_dir}/{ec.file_name}'
]
# render page
data = ["---", f"uuid: {ec.uuid}", "---", f"# {ec.name}"]
......@@ -582,8 +582,8 @@ class CollectionDoc(object):
new_files = 0
changed_files = 0
unmodified_files = 0
toc = []
ac_root = os.path.join(self.doc_root, "reference", "alarm-classes")
toc = ["- Overview: user/reference/alarm-classes/index.md"]
ac_root = os.path.join(self.doc_root, "user", "reference", "alarm-classes")
last_path: List[str] = []
indent: str = ""
for ac_name in sorted(self.alarm_class):
......@@ -607,7 +607,7 @@ class CollectionDoc(object):
last_path = path
short_name = ac_name.split(" | ")[-1].strip()
toc += [
f'{indent} - "{short_name}": reference/alarm-classes/{rel_dir}/{ac.file_name}'
f'{indent} - "{short_name}": user/reference/alarm-classes/{rel_dir}/{ac.file_name}'
]
# render page
data = ["---", f"uuid: {ac.uuid}", "---", f"# {ac.name}"]
......@@ -739,9 +739,9 @@ class CollectionDoc(object):
new_files = 0
changed_files = 0
unmodified_files = 0
ms_root = os.path.join(self.doc_root, "reference", "metrics", "scopes")
ms_root = os.path.join(self.doc_root, "user", "reference", "metrics", "scopes")
tab = "{{ tab }}"
toc = ["- Overview: reference/metrics/scopes/index.md"]
toc = ["- Overview: user/reference/metrics/scopes/index.md"]
for ms_name in sorted(self.metric_scope):
ms = self.metric_scope[ms_name]
data = ["---", f"uuid: {ms.uuid}", "---", f"# {ms.name} Metric Scope"]
......@@ -769,7 +769,7 @@ class CollectionDoc(object):
f"[{mt.field_name}]({mt_ref}) | {mt.field_type} | [{mq(mt.name)}]({mt_ref})"
]
data += [""]
toc += [f"- {ms.name}: reference/metrics/scopes/{ms.file_name}"]
toc += [f"- {ms.name}: user/reference/metrics/scopes/{ms.file_name}"]
page = "\n".join(data)
page_path = os.path.join(ms_root, ms.file_name)
to_write = False
......@@ -785,7 +785,7 @@ class CollectionDoc(object):
new_files += 1
to_write = True
if to_write:
print(f" Writing: reference/metrics/scopes/{ms.file_name}")
print(f" Writing: user/reference/metrics/scopes/{ms.file_name}")
with open(page_path, "w") as f:
f.write(page)
total_files = new_files + changed_files + unmodified_files
......@@ -799,10 +799,10 @@ class CollectionDoc(object):
new_files = 0
changed_files = 0
unmodified_files = 0
ms_root = os.path.join(self.doc_root, "reference", "metrics", "types")
ms_root = os.path.join(self.doc_root, "user", "reference", "metrics", "types")
last_path: List[str] = []
indent: str = ""
toc = ["- Overview: reference/metrics/types/index.md"]
toc = ["- Overview: user/reference/metrics/types/index.md"]
for mt_name in sorted(self.metric_type):
mt = self.metric_type[mt_name]
rel_dir = os.path.join(*mt.dir_path)
......@@ -824,7 +824,7 @@ class CollectionDoc(object):
last_path = path
short_name = mt_name.split(" | ")[-1].strip()
toc += [
f'{indent} - "{short_name}": reference/metrics/types/{rel_dir}/{mt.file_name}'
f'{indent} - "{short_name}": user/reference/metrics/types/{rel_dir}/{mt.file_name}'
]
# Render page
data = ["---", f"uuid: {mt.uuid}", "---", f"# {mt.name} Metric Type"]
......@@ -877,8 +877,8 @@ class CollectionDoc(object):
new_files = 0
changed_files = 0
unmodified_files = 0
mu_root = os.path.join(self.doc_root, "reference", "measurementunits")
toc = ["- Overview: reference/measurementunits/index.md"]
mu_root = os.path.join(self.doc_root, "user", "reference", "measurement-units")
toc = ["- Overview: user/reference/measurement-units/index.md"]
for mu_name in sorted(self.measurement_units):
mu = self.measurement_units[mu_name]
data = ["---", f"uuid: {mu.uuid}", "---", f"# {mu.name} Measurement Units"]
......@@ -909,7 +909,7 @@ class CollectionDoc(object):
for a in mu.alt_units
]
data += [""]
toc += [f"- {mu.name}: reference/measurementunits/{mu.file_name}"]
toc += [f"- {mu.name}: user/reference/measurement-units/{mu.file_name}"]
page = "\n".join(data)
page_path = os.path.join(mu_root, mu.file_name)
to_write = False
......@@ -925,7 +925,7 @@ class CollectionDoc(object):
new_files += 1
to_write = True
if to_write:
print(f" Writing: reference/measurementunits/{mu.file_name}")
print(f" Writing: user/reference/measurement-units/{mu.file_name}")
with open(page_path, "w") as f:
f.write(page)
total_files = new_files + changed_files + unmodified_files
......
......@@ -80,7 +80,8 @@ class ObjectAttr(EmbeddedDocument):
("sa.ManagedObject", "container"),
("inv.CoveredObject", "object"),
("inv.Object", "container"),
]
],
delete=[("inv.Sensor", "object")],
)
class Object(Document):
"""
......@@ -213,6 +214,8 @@ class Object(Document):
old_pop.update_pop_links()
if new_pop:
new_pop.update_pop_links()
if self.model.sensors:
self._sync_sensors()
@cachetools.cached(_path_cache, key=lambda x: str(x.id), lock=id_lock)
def get_path(self) -> List[str]:
......@@ -788,6 +791,15 @@ class Object(Document):
def reset_connection_interface(self, name):
self.connections = [c for c in self.connections if c.name != name]
def _sync_sensors(self):
"""
Synchronize sensors
:return:
"""
from .sensor import Sensor
Sensor.sync_object(self)
signals.pre_delete.connect(Object.detach_children, sender=Object)
signals.pre_delete.connect(Object.delete_disconnect, sender=Object)
......
# ----------------------------------------------------------------------
# Sensor model
# ----------------------------------------------------------------------
# Copyright (C) 2007-2020 The NOC Project
# See LICENSE for details
# ----------------------------------------------------------------------
# Python modules
import logging
from threading import Lock
import operator
from typing import Dict
# Third-party modules
from mongoengine.document import Document
from mongoengine.fields import StringField, IntField, LongField
import cachetools
# NOC modules
from noc.wf.models.state import State
from noc.core.wf.decorator import workflow
from noc.core.bi.decorator import bi_sync
from noc.core.mongo.fields import PlainReferenceField, ForeignKeyField
from noc.inv.models.object import Object
from noc.sa.models.managedobject import ManagedObject
from noc.pm.models.measurementunits import MeasurementUnits
from .sensorprofile import SensorProfile
id_lock = Lock()
logger = logging.getLogger(__name__)
@bi_sync
@workflow
class Sensor(Document):
meta = {"collection": "sensors", "strict": False, "auto_create_index": False}
profile = PlainReferenceField(SensorProfile, default=SensorProfile.get_default_profile)
object = PlainReferenceField(Object)
managed_object = ForeignKeyField(ManagedObject)
local_id = StringField()
state = PlainReferenceField(State)
units = PlainReferenceField(MeasurementUnits)
label = StringField()
dashboard_label = StringField()
protocol = StringField(choices=["modbus_rtu", "modbus_ascii", "modbus_tcp", "snmp", "ipmi"])
modbus_register = IntField()
snmp_oid = StringField()
bi_id = LongField(unique=True)
_id_cache = cachetools.TTLCache(maxsize=100, ttl=60)
_bi_id_cache = cachetools.TTLCache(maxsize=100, ttl=60)
def __str__(self):
if self.object:
return "%s: %s" % (self.object, self.local_id)
return "%s: %s" % (self.units, self.local_id)
@classmethod
@cachetools.cachedmethod(operator.attrgetter("_id_cache"), lock=lambda _: id_lock)
def get_by_id(cls, id):
return Sensor.objects.filter(id=id).first()
@classmethod
@cachetools.cachedmethod(operator.attrgetter("_bi_id_cache"), lock=lambda _: id_lock)
def get_by_bi_id(cls, id):
return Sensor.objects.filter(bi_id=id).first()
@classmethod
def sync_object(cls, obj: Object) -> None:
"""
Synchronize sensors with object model
:param obj:
:return:
"""
# Get existing sensors
obj_sensors: Dict[str, Sensor] = {s.name: s for s in Sensor.objects.filter(object=obj.id)}
m_proto = [
d.value
for d in obj.get_effective_data()
if d.interface == "modbus" and d.attr == "type"
] or ["rtu"]
# Create new sensors
for sensor in obj.model.sensors:
if sensor.name in obj_sensors:
del obj_sensors[sensor.name]
continue
#
logger.info(
"[%s|%s] Creating new sensor '%s'", obj.name if obj else "-", "-", sensor.name
)
s = Sensor(
profile=SensorProfile.get_default_profile(),
object=obj,
local_id=sensor.name,
units=sensor.units,
)
# Get sensor protocol
if sensor.modbus_register:
if not m_proto:
continue
s.protocol = "modbus_%s" % m_proto[0].lower()
s.modbus_register = sensor.modbus_register
elif sensor.snmp_oid:
s.protocol = "snmp"
s.snmp_oid = sensor.snmp_oid
else:
logger.info(
"[%s|%s] Unknown sensor protocol '%s'",
obj.name if obj else "-",
"-",
sensor.name,
)
s.save()
# Notify missed sensors
for s in sorted(obj_sensors):
sensor = obj_sensors[s]
logger.info("[%s|%s] Sensor is missed '%s'", obj.name if obj else "-", "-", sensor.name)
sensor.fire_event("missed")
# ----------------------------------------------------------------------
# SensorProfile model
# ----------------------------------------------------------------------
# Copyright (C) 2007-2020 The NOC Project
# See LICENSE for details
# ----------------------------------------------------------------------
# NOC modules
from threading import Lock
import operator
# Third-party modules
from mongoengine.document import Document
from mongoengine.fields import StringField, ListField, LongField, BooleanField
import cachetools
# NOC modules
from noc.main.models.style import Style
from noc.wf.models.workflow import Workflow
from noc.core.model.decorator import on_delete_check
from noc.core.bi.decorator import bi_sync
from noc.core.mongo.fields import PlainReferenceField, ForeignKeyField
id_lock = Lock()
@bi_sync
@on_delete_check(check=[("inv.Sensor", "profile")])
class SensorProfile(Document):
meta = {"collection": "sensorprofiles", "strict": False, "auto_create_index": False}
name = StringField(unique=True)
description = StringField()
workflow = PlainReferenceField(Workflow)
style = ForeignKeyField(Style)
enable_collect = BooleanField(default=False)
tags = ListField(StringField())
bi_id = LongField(unique=True)
_id_cache = cachetools.TTLCache(maxsize=100, ttl=60)
_bi_id_cache = cachetools.TTLCache(maxsize=100, ttl=60)
_default_cache = cachetools.TTLCache(maxsize=100, ttl=60)
DEFAULT_PROFILE_NAME = "default"
DEFAULT_WORKDLOW_NAME = "Sensor Default"
def __str__(self):
return self.name
@classmethod
@cachetools.cachedmethod(operator.attrgetter("_id_cache"), lock=lambda _: id_lock)
def get_by_id(cls, id) -> "SensorProfile":
return SensorProfile.objects.filter(id=id).first()
@classmethod
@cachetools.cachedmethod(operator.attrgetter("_bi_id_cache"), lock=lambda _: id_lock)
def get_by_bi_id(cls, id) -> "SensorProfile":
return SensorProfile.objects.filter(bi_id=id).first()
@classmethod
@cachetools.cachedmethod(operator.attrgetter("_default_cache"), lock=lambda _: id_lock)
def get_default_profile(cls) -> "SensorProfile":
sp = SensorProfile.objects.filter(name=cls.DEFAULT_PROFILE_NAME).first()
if not sp:
sp = SensorProfile(
name=cls.DEFAULT_PROFILE_NAME,
workflow=Workflow.objects.filter(name=cls.DEFAULT_WORKDLOW_NAME).first(),
)
sp.save()
return sp
......@@ -28,6 +28,7 @@ id_lock = Lock()
("crm.SupplierProfile", "style"),
("sla.SLAProfile", "style"),
("inv.NetworkSegmentProfile", "style"),
("inv.SensorProfile", "style"),
("ip.Address", "style"),
("ip.AddressProfile", "style"),
("ip.PrefixProfile", "style"),
......
......@@ -176,6 +176,8 @@ _MODELS = {
"inv.ObjectLog": "noc.inv.models.objectlog.ObjectLog",
"inv.ObjectModel": "noc.inv.models.objectmodel.ObjectModel",
"inv.Platform": "noc.inv.models.platform.Platform",
"inv.Sensor": "noc.inv.models.sensor.Sensor",
"inv.SensorProfile": "noc.inv.models.sensor.SensorProfile",
"inv.SubInterface": "noc.inv.models.subinterface.SubInterface",
"inv.Technology": "noc.inv.models.technology.Technology",
"inv.ResourceGroup": "noc.inv.models.resourcegroup.ResourceGroup",
......
......@@ -16,6 +16,7 @@ from mongoengine.fields import StringField, IntField, UUIDField, ListField, Embe
import cachetools
# NOC modules
from noc.core.model.decorator import on_delete_check
from noc.core.prettyjson import to_json
from noc.core.text import quote_safe_path
......@@ -66,6 +67,7 @@ class EnumValue(EmbeddedDocument):
return {"key": self.key, "value": self.value}
@on_delete_check(check=[("inv.Sensor", "units")])
class MeasurementUnits(Document):
meta = {
"collection": "measurementunits",
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment