예제 #1
0
파일: node.py 프로젝트: fossabot/noc
class Node(nosql.Document):
    meta = {"collection": "noc.wf.nodes", "allow_inheritance": False}

    workflow = nosql.PlainReferenceField(Workflow)
    lane = nosql.PlainReferenceField(Lane)
    name = nosql.StringField()
    label = nosql.StringField()
    description = nosql.StringField()
    handler = nosql.StringField()
    # param -> value
    params = nosql.RawDictField()
    # Connections
    next_node = nosql.PlainReferenceField("self")
    next_true_node = nosql.PlainReferenceField("self")
    next_false_node = nosql.PlainReferenceField("self")
    # Graph position
    x = nosql.IntField()
    y = nosql.IntField()

    def __unicode__(self):
        return "%s %s" % (self.workflow, self.name)

    @property
    def handler_class(self):
        m = __import__("noc.wf.handlers", {}, {}, str(self.handler))
        return getattr(m, "%sHandler" % self.handler)
예제 #2
0
class AlarmRootCauseCondition(nosql.EmbeddedDocument):
    meta = {
        "strict": False,
        "auto_create_index": False
    }

    name = nosql.StringField(required=True)
    root = nosql.PlainReferenceField("fm.AlarmClass")
    window = nosql.IntField(required=True)
    condition = nosql.StringField(default="True")
    match_condition = nosql.DictField(required=True)

    def __unicode__(self):
        return self.name

    def __eq__(self, other):
        return (
            self.name == other.name and
            (
                (self.root is None and other.root is None) or
                (
                    self.root and other.root and
                    self.root.id == other.root.id
                )
            ) and
            self.window == other.window and
            self.condition == other.condition and
            self.match_condition == other.match_condition
        )
예제 #3
0
class Overlay(nosql.Document):
    meta = {"collection": "noc.gis.overlays", "allow_inheritance": False}

    name = nosql.StringField(required=True)
    gate_id = nosql.StringField(unique=True)
    is_active = nosql.BooleanField(required=True, default=True)
    permission_name = nosql.StringField(required=True)
    overlay = nosql.StringField(required=True)
    config = nosql.DictField()

    _overlay_cache = {}  # name -> Overlay

    def __unicode__(self):
        return self.name

    def get_overlay(self):
        if self.overlay not in self._overlay_cache:
            from noc.gis.overlays.base import OverlayHandler
            m = __import__("noc.gis.overlays.%s" % self.overlay, {}, {}, "*")
            for n in dir(m):
                o = getattr(m, n)
                if (inspect.isclass(o) and o != OverlayHandler
                        and issubclass(o, OverlayHandler)):
                    self._overlay_cache[self.overlay] = o
                    break
        h = self._overlay_cache[self.overlay]
        return h(**self.config)
예제 #4
0
class Workflow(nosql.Document):
    meta = {"collection": "noc.wf.workflows", "allow_inheritance": False}

    # Unique identifier
    name = nosql.StringField()
    # Long name
    display_name = nosql.StringField()
    solution = nosql.PlainReferenceField(Solution)
    version = nosql.IntField()
    is_active = nosql.BooleanField()
    description = nosql.StringField()
    #
    start_node = nosql.StringField()
    # Permissions
    # stat_permission = nosql.StringField()
    # trace_permission = nosql.StringField()
    # kill_permission = nosql.StringField()
    trace = nosql.BooleanField(default=False)

    def __unicode__(self):
        return "%s.%s v%s" % (self.solution.name, self.name, self.version)

    def get_node(self, name):
        return Node.objects.filter(workflow=self.id, name=name).first()

    def get_start_node(self):
        return Node.objects.filter(workflow=self.id,
                                   id=self.start_node).first()

    def run(self, _trace=None, **kwargs):
        """
        Run process
        :param kwargs:
        :return: Process instance
        """
        # Find start node
        start_node = self.get_start_node()
        if not start_node:
            raise InvalidStartNode(self.start_node)
        #
        trace = self.trace if _trace is None else _trace
        # Prepare context
        ctx = {}
        for v in Variable.objects.filter(workflow=self.id):
            if v.name in kwargs:
                ctx[v.name] = v.clean(kwargs[v.name])
            elif v.default:
                ctx[v.name] = v.clean(v.default)
            else:
                ctx[v.name] = None

        p = Process(workflow=self,
                    context=ctx,
                    start_time=datetime.datetime.now(),
                    node=start_node,
                    trace=trace)
        p.save()
        # Schedule job
        p.schedule()
        return p
예제 #5
0
파일: variable.py 프로젝트: fossabot/noc
class Variable(nosql.Document):
    meta = {"collection": "noc.wf.variables", "allow_inheritance": False}

    workflow = nosql.PlainReferenceField(Workflow)
    name = nosql.StringField()
    type = nosql.StringField(
        choices=[("str", "String"), ("int",
                                     "Integer"), ("bool",
                                                  "Boolean"), ("float",
                                                               "Float")])
    default = nosql.StringField()
    # Required to start the process
    required = nosql.BooleanField()
    description = nosql.StringField()

    def __unicode__(self):
        return "%s %s (%s)" % (self.workflow, self.name, self.type)

    def clean(self, value):
        return getattr(self, "clean_%s" % self.type)(value)

    def clean_str(self, value):
        return value

    def clean_int(self, value):
        return int(value)

    def clean_bool(self, value):
        return value.lower() in ["on", "true", "yes"]

    def clean_float(self, value):
        return float(value)
예제 #6
0
class Map(nosql.Document):
    meta = {
        "collection": "noc.gis.maps",
        "strict": False,
        "auto_create_index": False
    }
    name = nosql.StringField(unique=True)
    is_builtin = nosql.BooleanField(default=True)
    is_active = nosql.BooleanField(default=True)
    # srs = nosql.ForeignKeyField(SRS)
    layers = nosql.ListField(nosql.StringField())

    def __unicode__(self):
        return self.name

    @property
    def active_layers(self):
        """
        Get list of active layers
        :return:
        """
        r = []
        for ln in self.layers:
            l = Layer.objects.filter(name=ln).first()
            if l and l.is_active:
                r += [l]
        return r
예제 #7
0
파일: __init__.py 프로젝트: fossabot/noc
class UserState(nosql.Document):
    meta = {"collection": "noc.userstate", "allow_inheritance": False}
    user_id = nosql.IntField()
    key = nosql.StringField()
    value = nosql.StringField()

    def __unicode__(self):
        return "%s: %s" % (self.user_id, self.key)
예제 #8
0
class FontSet(nosql.Document):
    meta = {"collection": "noc.gis.fontsets", "allow_inheritance": False}
    name = nosql.StringField(unique=True)
    is_builtin = nosql.BooleanField(default=True)
    description = nosql.StringField(required=False)
    fonts = nosql.ListField(nosql.StringField())

    def __unicode__(self):
        return self.name
예제 #9
0
class Layer(nosql.Document):
    meta = {"collection": "noc.gis.layers", "allow_inheritance": False}
    name = nosql.StringField(unique=True)
    is_builtin = nosql.BooleanField(default=True)
    is_active = nosql.BooleanField(default=True)
    #srs = nosql.ForeignKeyField(SRS)
    styles = nosql.ListField(nosql.StringField())
    datasource = nosql.DictField()

    def __unicode__(self):
        return self.name
예제 #10
0
class DataSource(nosql.EmbeddedDocument):
    meta = {"allow_inheritance": False}
    name = nosql.StringField()
    datasource = nosql.StringField()
    search = nosql.DictField()

    def __unicode__(self):
        return self.name

    def __eq__(self, other):
        return (self.name == other.name and self.datasource == other.datasource
                and self.search == other.search)
예제 #11
0
파일: datasource.py 프로젝트: skripkar/noc
class DataSource(nosql.EmbeddedDocument):
    meta = {"strict": False, "auto_create_index": False}
    name = nosql.StringField()
    datasource = nosql.StringField()
    search = nosql.DictField()

    def __unicode__(self):
        return self.name

    def __eq__(self, other):
        return (self.name == other.name and self.datasource == other.datasource
                and self.search == other.search)
예제 #12
0
파일: alarmlog.py 프로젝트: fossabot/noc
class AlarmLog(nosql.EmbeddedDocument):
    meta = {"allow_inheritance": False}
    timestamp = nosql.DateTimeField()
    from_status = nosql.StringField(max_length=1,
                                    regex=r"^[AC]$",
                                    required=True)
    to_status = nosql.StringField(max_length=1, regex=r"^[AC]$", required=True)
    message = nosql.StringField()

    def __unicode__(self):
        return u"%s [%s -> %s]: %s" % (self.timestamp, self.from_status,
                                       self.to_status, self.message)
예제 #13
0
class AlarmClassVar(nosql.EmbeddedDocument):
    meta = {"allow_inheritance": False}
    name = nosql.StringField(required=True)
    description = nosql.StringField(required=False)
    default = nosql.StringField(required=False)

    def __unicode__(self):
        return self.name

    def __eq__(self, other):
        return (self.name == other.name
                and self.description == other.description
                and self.default == other.default)
예제 #14
0
class AlarmClassVar(nosql.EmbeddedDocument):
    meta = {"strict": False, "auto_create_index": False}
    name = nosql.StringField(required=True)
    description = nosql.StringField(required=False)
    default = nosql.StringField(required=False)

    def __unicode__(self):
        return self.name

    def __eq__(self, other):
        return (self.name == other.name
                and self.description == other.description
                and self.default == other.default)
예제 #15
0
class Area(nosql.Document):
    meta = {"allow_inheritance": False, "collection": "noc.gis.areas"}

    name = nosql.StringField()
    is_active = nosql.BooleanField(default=True)
    min_zoom = nosql.IntField(default=0)
    max_zoom = nosql.IntField(default=18)
    # (EPSG:4326) coordinates
    SW = nosql.GeoPointField()
    NE = nosql.GeoPointField()
    description = nosql.StringField(required=False)

    def __unicode__(self):
        return self.name
예제 #16
0
파일: mibdata.py 프로젝트: fossabot/noc
class MIBData(nosql.Document):
    meta = {
        "collection": "noc.mibdata",
        "allow_inheritance": False,
        "indexes": ["oid", "name", "mib", "aliases"]
    }
    mib = nosql.PlainReferenceField(MIB)
    oid = nosql.StringField(required=True, unique=True)
    name = nosql.StringField(required=True)
    description = nosql.StringField(required=False)
    syntax = nosql.DictField(required=False)
    aliases = nosql.ListField(nosql.StringField(), default=[])

    def __unicode__(self):
        return self.name
예제 #17
0
파일: alarmlog.py 프로젝트: 0pt1on/noc
class AlarmLog(nosql.EmbeddedDocument):
    meta = {"strict": False, "auto_create_index": False}
    timestamp = nosql.DateTimeField()
    from_status = nosql.StringField(max_length=1,
                                    regex=r"^[AC]$",
                                    required=True)
    to_status = nosql.StringField(max_length=1, regex=r"^[AC]$", required=True)
    message = nosql.StringField()

    def __str__(self):
        return "%s [%s -> %s]: %s" % (
            self.timestamp,
            self.from_status,
            self.to_status,
            self.message,
        )
예제 #18
0
class Style(nosql.Document):
    meta = {"collection": "noc.gis.styles", "allow_inheritance": False}
    name = nosql.StringField(unique=True)
    is_builtin = nosql.BooleanField(default=True)
    rules = nosql.ListField(nosql.EmbeddedDocumentField(Rule))

    def __unicode__(self):
        return self.name
예제 #19
0
class Lane(nosql.Document):
    meta = {"collection": "noc.wf.lanes", "allow_inheritance": False}

    workflow = nosql.PlainReferenceField(Workflow)
    name = nosql.StringField()
    is_active = nosql.BooleanField()

    def __unicode__(self):
        return "%s %s" % (self.workflow, self.name)
예제 #20
0
파일: solution.py 프로젝트: fossabot/noc
class Solution(nosql.Document):
    meta = {"collection": "noc.wf.solutions", "allow_inheritance": False}

    name = nosql.StringField()
    version = nosql.IntField(default=1)
    is_active = nosql.BooleanField(default=False)
    description = nosql.StringField()

    def __unicode__(self):
        return "%s v%s" % (self.name, self.version)

    @classmethod
    def get_active(cls, name):
        """
        Get last version of active solution
        :param cls:
        :param name: Solution name
        :return: Solution or None
        """
        return cls.objects.filter(name=name, is_active=True)\
            .order_by("-version").first()
예제 #21
0
class Rule(nosql.EmbeddedDocument):
    meta = {"allow_inheritance": False}
    minscale_zoom = nosql.IntField(required=False)
    maxscale_zoom = nosql.IntField(required=False)
    rule_filter = nosql.StringField(required=False)
    symbolizers = nosql.ListField(nosql.DictField())

    def __unicode__(self):
        return unicode(id(self))

    def __eq__(self, other):
        return (self.minscale_zoom == other.minscale_zoom
                and self.maxscale_zoom == other.maxscale_zoom
                and self.rule_filter == other.rule_filter
                and self.symbolizers == other.symbolizers)
예제 #22
0
class AlarmClassJob(nosql.EmbeddedDocument):
    meta = {"allow_inheritance": False}
    # Job name (fm/correlator/jobs/<name>.py)
    job = nosql.StringField(required=True)
    # Start job after *delay* seconds after alarm risen
    # delay = nosql.IntField(required=False, default=0)
    # Restart job every *interval* seconds
    interval = nosql.IntField(required=False, default=0)
    # Job parameters: name -> expression
    vars = nosql.DictField(required=True)

    def __unicode__(self):
        return self.job

    def __eq__(self, other):
        return (self.job == other.job and self.interval == other.interval
                and self.vars == other.vars)
예제 #23
0
파일: __init__.py 프로젝트: fossabot/noc
class UserSession(nosql.Document):
    meta = {"collection": "noc.user_sessions", "allow_inheritance": False}
    session_key = nosql.StringField(primary_key=True)
    user_id = nosql.IntField()

    @classmethod
    def register(cls, session_key, user):
        UserSession(session_key=session_key,
                    user_id=user.id).save(force_insert=True)

    @classmethod
    def unregister(cls, session_key):
        UserSession.objects.filter(session_key=session_key).delete()

    @classmethod
    def active_sessions(cls, user=None, group=None):
        """
        Calculate current active sessions for user and group
        """
        ids = []
        if user:
            ids += [user.id]
        if group:
            ids += group.user_set.values_list("id", flat=True)
        n = 0
        now = datetime.datetime.now()
        for us in UserSession.objects.filter(user_id__in=ids):
            s = MongoSession.objects.filter(session_key=us.session_key).first()
            if s:
                # Session exists
                if s.expire_date < now:
                    # Expired session
                    s.delete()
                else:
                    n += 1  # Count as active
            else:
                # Hanging session, schedule to kill
                us.delete()
        return n
예제 #24
0
class AlarmClassCategory(nosql.Document):
    meta = {
        "collection": "noc.alartmclasscategories",  # @todo: Fix bug
        "allow_inheritance": False
    }
    name = nosql.StringField()
    parent = nosql.ObjectIdField(required=False)

    def __unicode__(self):
        return self.name

    def save(self, *args, **kwargs):
        if " | " in self.name:
            p_name = " | ".join(self.name.split(" | ")[:-1])
            p = AlarmClassCategory.objects.filter(name=p_name).first()
            if not p:
                p = AlarmClassCategory(name=p_name)
                p.save()
            self.parent = p.id
        else:
            self.parent = None
        super(AlarmClassCategory, self).save(*args, **kwargs)
예제 #25
0
class ActiveAlarm(nosql.Document):
    meta = {
        "collection": "noc.alarms.active",
        "strict": False,
        "auto_create_index": False,
        "indexes": [
            "timestamp", "root", "-severity",
            ("alarm_class", "managed_object"),
            ("discriminator", "managed_object"),
            ("timestamp", "managed_object"),
            "escalation_tt",
            "escalation_ts",
            "adm_path",
            "segment_path",
            "container_path",
            "uplinks"
        ]
    }
    status = "A"

    timestamp = nosql.DateTimeField(required=True)
    last_update = nosql.DateTimeField(required=True)
    managed_object = nosql.ForeignKeyField(ManagedObject)
    alarm_class = nosql.PlainReferenceField(AlarmClass)
    severity = nosql.IntField(required=True)
    vars = nosql.DictField()
    # Calculated alarm discriminator
    # Has meaning only for alarms with is_unique flag set
    # Calculated as sha1("value1\x00....\x00valueN").hexdigest()
    discriminator = nosql.StringField(required=False)
    log = nosql.ListField(nosql.EmbeddedDocumentField(AlarmLog))
    # Responsible person
    owner = nosql.ForeignKeyField(User, required=False)
    #
    opening_event = nosql.ObjectIdField(required=False)
    closing_event = nosql.ObjectIdField(required=False)
    # List of subscribers
    subscribers = nosql.ListField(nosql.ForeignKeyField(User))
    #
    custom_subject = nosql.StringField(required=False)
    custom_style = nosql.ForeignKeyField(Style, required=False)
    #
    reopens = nosql.IntField(required=False)
    # RCA
    # Reference to root cause (Active Alarm or Archived Alarm instance)
    root = nosql.ObjectIdField(required=False)
    # Escalated TT ID in form
    # <external system name>:<external tt id>
    escalation_ts = nosql.DateTimeField(required=False)
    escalation_tt = nosql.StringField(required=False)
    escalation_error = nosql.StringField(required=False)
    # span context
    escalation_ctx = nosql.LongField(required=False)
    # Close tt when alarm cleared
    close_tt = nosql.BooleanField(default=False)
    # Do not clear alarm until *wait_tt* is closed
    wait_tt = nosql.StringField()
    wait_ts = nosql.DateTimeField()
    # Directly affected services summary, grouped by profiles
    # (connected to the same managed object)
    direct_services = nosql.ListField(nosql.EmbeddedDocumentField(SummaryItem))
    direct_subscribers = nosql.ListField(nosql.EmbeddedDocumentField(SummaryItem))
    # Indirectly affected services summary, groupped by profiles
    # (covered by this and all inferred alarms)
    total_objects = nosql.ListField(nosql.EmbeddedDocumentField(ObjectSummaryItem))
    total_services = nosql.ListField(nosql.EmbeddedDocumentField(SummaryItem))
    total_subscribers = nosql.ListField(nosql.EmbeddedDocumentField(SummaryItem))
    # Template and notification group to send close notification
    clear_template = nosql.ForeignKeyField(Template, required=False)
    clear_notification_group = nosql.ForeignKeyField(NotificationGroup, required=False)
    # Paths
    adm_path = nosql.ListField(nosql.IntField())
    segment_path = nosql.ListField(nosql.ObjectIdField())
    container_path = nosql.ListField(nosql.ObjectIdField())
    # Uplinks, for topology_rca only
    uplinks = nosql.ListField(nosql.IntField())

    def __unicode__(self):
        return u"%s" % self.id

    def iter_changed_datastream(self):
        if config.datastream.enable_alarm:
            yield "alarm", self.id

    def clean(self):
        super(ActiveAlarm, self).clean()
        if not self.last_update:
            self.last_update = self.timestamp
        data = self.managed_object.data
        self.adm_path = data.adm_path
        self.segment_path = data.segment_path
        self.container_path = data.container_path
        self.uplinks = data.uplinks

    def safe_save(self, **kwargs):
        """
        Create new alarm or update existing if still exists
        :param kwargs:
        :return:
        """
        if self.id:
            # Update existing only if exists
            if "save_condition" not in kwargs:
                kwargs["save_condition"] = {"id": self.id}
            try:
                self.save(**kwargs)
            except SaveConditionError:
                pass  # Race condition, closed during update
        else:
            self.save()

    def change_severity(self, user="", delta=None, severity=None, to_save=True):
        """
        Change alarm severity
        """
        if isinstance(user, User):
            user = user.username
        if delta:
            self.severity = max(0, self.severity + delta)
            if delta > 0:
                self.log_message(
                    "%s has increased alarm severity by %s" % (
                        user, delta))
            else:
                self.log_message(
                    "%s has decreased alarm severity by %s" % (
                        user, delta))
        elif severity:
            if type(severity) in (int, long, float):
                self.severity = int(severity)
                self.log_message(
                    "%s has changed severity to %s" % (user, severity))
            else:
                self.severity = severity.severity
                self.log_message(
                    "%s has changed severity to %s" % (user, severity.name))
        if to_save:
            self.safe_save()

    def log_message(self, message, to_save=True):
        self.log += [AlarmLog(timestamp=datetime.datetime.now(),
                     from_status=self.status, to_status=self.status,
                     message=message)]
        if to_save:
            self.safe_save()

    def clear_alarm(self, message, ts=None, force=False):
        """
        Clear alarm
        :param message: Log clearing message
        :param ts: Clearing timestamp
        :param force: Clear ever if wait_tt seg
        """
        ts = ts or datetime.datetime.now()
        if self.wait_tt and not force:
            # Wait for escalated tt to close
            if not self.wait_ts:
                self.wait_ts = ts
                self.log_message("Waiting for TT to close")
                call_later(
                    "noc.services.escalator.wait_tt.wait_tt",
                    scheduler="escalator",
                    pool=self.managed_object.escalator_shard,
                    alarm_id=self.id
                )
            return
        if self.alarm_class.clear_handlers:
            # Process clear handlers
            for h in self.alarm_class.get_clear_handlers():
                try:
                    h(self)
                except Exception:
                    error_report()
        log = self.log + [AlarmLog(timestamp=ts, from_status="A",
                                   to_status="C", message=message)]
        a = ArchivedAlarm(
            id=self.id,
            timestamp=self.timestamp,
            clear_timestamp=ts,
            managed_object=self.managed_object,
            alarm_class=self.alarm_class,
            severity=self.severity,
            vars=self.vars,
            log=log,
            root=self.root,
            escalation_ts=self.escalation_ts,
            escalation_tt=self.escalation_tt,
            escalation_error=self.escalation_error,
            escalation_ctx=self.escalation_ctx,
            opening_event=self.opening_event,
            closing_event=self.closing_event,
            discriminator=self.discriminator,
            reopens=self.reopens,
            direct_services=self.direct_services,
            direct_subscribers=self.direct_subscribers,
            total_objects=self.total_objects,
            total_services=self.total_services,
            total_subscribers=self.total_subscribers,
            adm_path=self.adm_path,
            segment_path=self.segment_path,
            container_path=self.container_path,
            uplinks=self.uplinks
        )
        ct = self.alarm_class.get_control_time(self.reopens)
        if ct:
            a.control_time = datetime.datetime.now() + datetime.timedelta(seconds=ct)
        a.save()
        # Send notifications
        if not a.root and not self.reopens:
            a.managed_object.event(a.managed_object.EV_ALARM_CLEARED, {
                "alarm": a,
                "subject": a.subject,
                "body": a.body,
                "symptoms": a.alarm_class.symptoms,
                "recommended_actions": a.alarm_class.recommended_actions,
                "probable_causes": a.alarm_class.probable_causes
            })
        elif ct:
            pass
        # Set checks on all consequences
        for d in self._get_collection().find({
            "root": self.id
        }, {"_id": 1, "alarm_class": 1}):
            ac = AlarmClass.get_by_id(d["alarm_class"])
            if not ac:
                continue
            t = ac.recover_time
            if not t:
                continue
            call_later(
                "noc.services.correlator.check.check_close_consequence",
                scheduler="correlator",
                pool=self.managed_object.pool.name,
                delay=t,
                alarm_id=d["_id"]
            )
        # Clear alarm
        self.delete()
        # Close TT
        # MUST be after .delete() to prevent race conditions
        if a.escalation_tt or self.clear_template:
            if self.clear_template:
                ctx = {
                    "alarm": a
                }
                subject = self.clear_template.render_subject(**ctx)
                body = self.clear_template.render_body(**ctx)
            else:
                subject = "Alarm cleared"
                body = "Alarm has been cleared"
            call_later(
                "noc.services.escalator.escalation.notify_close",
                scheduler="escalator",
                pool=self.managed_object.escalator_shard,
                max_runs=ALARM_CLOSE_RETRIES,
                alarm_id=self.id,
                tt_id=self.escalation_tt,
                subject=subject,
                body=body,
                notification_group_id=self.clear_notification_group.id if self.clear_notification_group else None,
                close_tt=self.close_tt
            )
        # Gather diagnostics
        AlarmDiagnosticConfig.on_clear(a)
        # Return archived
        return a

    def get_template_vars(self):
        """
        Prepare template variables
        """
        vars = self.vars.copy()
        vars.update({"alarm": self})
        return vars

    @property
    def subject(self):
        if self.custom_subject:
            s = self.custom_subject
        else:
            ctx = Context(self.get_template_vars())
            s = DjangoTemplate(self.alarm_class.subject_template).render(ctx)
        if len(s) >= 255:
            s = s[:125] + " ... " + s[-125:]
        return s

    @property
    def body(self):
        ctx = Context(self.get_template_vars())
        s = DjangoTemplate(self.alarm_class.body_template).render(ctx)
        return s

    def change_owner(self, user):
        """
        Change alarm's owner
        """
        self.owner = user
        self.save()

    def subscribe(self, user):
        """
        Change alarm's subscribers
        """
        if user.id not in self.subscribers:
            self.subscribers += [user.id]
            self.log_message("%s(%s) has been subscribed" % (
                (" ".join([user.first_name, user.last_name]),
                 user.username)
            ), to_save=False)
            self.save()

    def unsubscribe(self, user):
        if self.is_subscribed(user):
            self.subscribers = [u.id for u in self.subscribers
                                if u != user.id]
            self.log_message("%s(%s) has been unsubscribed" % (
                (" ".join([user.first_name, user.last_name]),
                 user.username)
            ), to_save=False)
            self.save()

    def is_owner(self, user):
        return self.owner == user

    def is_subscribed(self, user):
        return user.id in self.subscribers

    @property
    def is_unassigned(self):
        return self.owner is None

    @property
    def duration(self):
        dt = datetime.datetime.now() - self.timestamp
        return dt.days * 86400 + dt.seconds

    @property
    def display_duration(self):
        duration = datetime.datetime.now() - self.timestamp
        secs = duration.seconds % 60
        mins = (duration.seconds / 60) % 60
        hours = (duration.seconds / 3600) % 24
        days = duration.days
        r = "%02d:%02d:%02d" % (hours, mins, secs)
        if days:
            r = "%dd %s" % (days, r)
        return r

    @property
    def effective_style(self):
        if self.custom_style:
            return self.custom_style
        else:
            return AlarmSeverity.get_severity(self.severity).style

    def get_root(self):
        """
        Get top-level root alarm
        """
        root = self
        while root.root:
            root = get_alarm(root.root)
        return root

    def update_summary(self):
        def update_dict(d1, d2):
            for k in d2:
                if k in d1:
                    d1[k] += d2[k]
                else:
                    d1[k] = d2[k]

        services = SummaryItem.items_to_dict(self.direct_services)
        subscribers = SummaryItem.items_to_dict(self.direct_subscribers)
        objects = {
            self.managed_object.object_profile.id: 1
        }

        for a in ActiveAlarm.objects.filter(root=self.id):
            a.update_summary()
            update_dict(
                objects,
                SummaryItem.items_to_dict(a.total_objects)
            )
            update_dict(
                services,
                SummaryItem.items_to_dict(a.total_services)
            )
            update_dict(
                subscribers,
                SummaryItem.items_to_dict(a.total_subscribers)
            )
        obj_list = ObjectSummaryItem.dict_to_items(objects)
        svc_list = SummaryItem.dict_to_items(services)
        sub_list = SummaryItem.dict_to_items(subscribers)
        if svc_list != self.total_services or sub_list != self.total_subscribers or obj_list != self.total_objects:
            ns = ServiceSummary.get_severity({
                "service": services,
                "subscriber": subscribers,
                "objects": objects
            })
            self.total_objects = obj_list
            self.total_services = svc_list
            self.total_subscribers = sub_list
            if ns != self.severity:
                self.change_severity(severity=ns, to_save=False)
            self.safe_save()

    def set_root(self, root_alarm):
        """
        Set root cause
        """
        if self.root:
            return
        if self.id == root_alarm.id:
            raise Exception("Cannot set self as root cause")
        # Detect loop
        root = root_alarm
        while root and root.root:
            root = root.root
            if root == self.id:
                return
            root = get_alarm(root)
        # Set root
        self.root = root_alarm.id
        self.log_message(
            "Alarm %s has been marked as root cause" % root_alarm.id)
        # self.save()  Saved by log_message
        root_alarm.log_message(
            "Alarm %s has been marked as child" % self.id)
        root_alarm.update_summary()
        # Clear pending notifications
        # Notification.purge_delayed("alarm:%s" % self.id)

    def escalate(self, tt_id, close_tt=False):
        self.escalation_tt = tt_id
        self.escalation_ts = datetime.datetime.now()
        self.close_tt = close_tt
        self.log_message("Escalated to %s" % tt_id)
        q = {"_id": self.id}
        op = {
            "$set": {
                "escalation_tt": self.escalation_tt,
                "escalation_ts": self.escalation_ts,
                "close_tt": self.close_tt,
                "escalation_error": None
            }
        }
        r = ActiveAlarm._get_collection().update_one(q, op)
        if r.acknowledged and not r.modified_count:
            # Already closed, update archive
            ArchivedAlarm._get_collection().update_one(q, op)

    def set_escalation_error(self, error):
        self.escalation_error = error
        self._get_collection().update_one(
            {"_id": self.id},
            {"$set": {
                "escalation_error": error
            }}
        )

    def set_escalation_context(self):
        current_context, current_span = get_current_span()
        if current_context or self.escalation_ctx:
            self.escalation_ctx = current_context
            self._get_collection().update_one(
                {"_id": self.id},
                {"$set": {
                    "escalation_ctx": current_context
                }}
            )

    def set_clear_notification(self, notification_group, template):
        self.clear_notification_group = notification_group
        self.clear_template = template
        self.safe_save(save_condition={
            "managed_object": {
                "$exists": True
            },
            "id": self.id
        })

    def iter_consequences(self):
        """
        Generator yielding all consequences alarm
        """
        for a in ActiveAlarm.objects.filter(root=self.id):
            yield a
            for ca in a.iter_consequences():
                yield ca

    def iter_affected(self):
        """
        Generator yielding all affected managed objects
        """
        seen = set([self.managed_object])
        yield self.managed_object
        for a in self.iter_consequences():
            if a.managed_object not in seen:
                seen.add(a.managed_object)
                yield a.managed_object

    def iter_escalated(self):
        """
        Generator yielding all escalated consequences
        """
        for a in self.iter_consequences():
            if a.escalation_tt:
                yield a
예제 #26
0
파일: activealarm.py 프로젝트: fossabot/noc
class ActiveAlarm(nosql.Document):
    meta = {
        "collection":
        "noc.alarms.active",
        "allow_inheritance":
        False,
        "indexes": [
            "timestamp", "discriminator", "root", "-severity", "alarm_class",
            ("timestamp", "managed_object")
        ]
    }
    status = "A"

    timestamp = nosql.DateTimeField(required=True)
    last_update = nosql.DateTimeField(required=True)
    managed_object = nosql.ForeignKeyField(ManagedObject)
    alarm_class = nosql.PlainReferenceField(AlarmClass)
    severity = nosql.IntField(required=True)
    vars = nosql.DictField()
    # Calculated alarm discriminator
    # Has meaning only for alarms with is_unique flag set
    # Calculated as sha1("value1\x00....\x00valueN").hexdigest()
    discriminator = nosql.StringField(required=False)
    log = nosql.ListField(nosql.EmbeddedDocumentField(AlarmLog))
    # Responsible person
    owner = nosql.ForeignKeyField(User, required=False)
    #
    opening_event = nosql.ObjectIdField(required=False)
    closing_event = nosql.ObjectIdField(required=False)
    # List of subscribers
    subscribers = nosql.ListField(nosql.ForeignKeyField(User))
    #
    custom_subject = nosql.StringField(required=False)
    custom_style = nosql.ForeignKeyField(Style, required=False)
    #
    reopens = nosql.IntField(required=False)
    # RCA
    # Reference to root cause (Active Alarm or Archived Alarm instance)
    root = nosql.ObjectIdField(required=False)

    def __unicode__(self):
        return u"%s" % self.id

    def save(self, *args, **kwargs):
        if not self.last_update:
            self.last_update = self.timestamp
        return super(ActiveAlarm, self).save(*args, **kwargs)

    def _change_root_severity(self):
        """
        Change root severity, when necessary
        """
        if not self.root:
            return
        root = get_alarm(self.root)
        if root and root.severity < self.severity:
            root.change_severity(self.severity)
            root.log_message("Severity has been increased by child alarm %s" %
                             self.id)

    def change_severity(self, user="", delta=None, severity=None):
        """
        Change alarm severity
        """
        if isinstance(user, User):
            user = user.username
        if delta:
            self.severity = max(0, self.severity + delta)
            if delta > 0:
                self.log_message("%s has increased alarm severity by %s" %
                                 (user, delta))
            else:
                self.log_message("%s has decreased alarm severity by %s" %
                                 (user, delta))
        elif severity:
            self.severity = severity.severity
            self.log_message("%s has changed severity to %s" %
                             (user, severity.name))
        self._change_root_severity()
        self.save()

    def log_message(self, message, to_save=True):
        self.log += [
            AlarmLog(timestamp=datetime.datetime.now(),
                     from_status=self.status,
                     to_status=self.status,
                     message=message)
        ]
        if to_save:
            self.save()

    def contribute_event(self, e, open=False, close=False):
        # Set opening event when necessary
        if open:
            self.opening_event = e.id
        # Set closing event when necessary
        if close:
            self.closing_event = e.id
        # Update timestamp
        if e.timestamp < self.timestamp:
            self.timestamp = e.timestamp
        else:
            self.last_update = max(self.last_update, e.timestamp)
        self.save()
        # Update event's list of alarms
        if self.id not in e.alarms:
            e.alarms.append(self.id)
            e.save()

    def clear_alarm(self, message):
        ts = datetime.datetime.now()
        log = self.log + [
            AlarmLog(
                timestamp=ts, from_status="A", to_status="C", message=message)
        ]
        a = ArchivedAlarm(id=self.id,
                          timestamp=self.timestamp,
                          clear_timestamp=ts,
                          managed_object=self.managed_object,
                          alarm_class=self.alarm_class,
                          severity=self.severity,
                          vars=self.vars,
                          log=log,
                          root=self.root,
                          opening_event=self.opening_event,
                          closing_event=self.closing_event,
                          discriminator=self.discriminator,
                          reopens=self.reopens)
        ct = self.alarm_class.get_control_time(self.reopens)
        if ct:
            a.control_time = datetime.datetime.now() + datetime.timedelta(
                seconds=ct)
        a.save()
        # @todo: Clear related correlator jobs
        self.delete()
        # Send notifications
        if not a.root and not self.reopens:
            a.managed_object.event(
                a.managed_object.EV_ALARM_CLEARED, {
                    "alarm": a,
                    "subject": a.subject,
                    "body": a.body,
                    "symptoms": a.alarm_class.symptoms,
                    "recommended_actions": a.alarm_class.recommended_actions,
                    "probable_causes": a.alarm_class.probable_causes
                })
        elif ct:
            # Schedule delayed job
            submit_job("fm.correlator",
                       "control_notify",
                       key=a.id,
                       ts=a.control_time)
        return a

    def get_template_vars(self):
        """
        Prepare template variables
        """
        vars = self.vars.copy()
        vars.update({"alarm": self})
        return vars

    @property
    def subject(self):
        ctx = Context(self.get_template_vars())
        s = Template(self.alarm_class.subject_template).render(ctx)
        if len(s) >= 255:
            s = s[:125] + " ... " + s[-125:]
        return s

    @property
    def body(self):
        ctx = Context(self.get_template_vars())
        s = Template(self.alarm_class.body_template).render(ctx)
        return s

    def change_owner(self, user):
        """
        Change alarm's owner
        """
        self.owner = user
        self.save()

    def subscribe(self, user):
        """
        Change alarm's subscribers
        """
        if user.id not in self.subscribers:
            self.subscribers += [user.id]
            self.log_message(
                "%s(%s) has been subscribed" %
                ((" ".join([user.first_name, user.last_name]), user.username)),
                to_save=False)
            self.save()

    def unsubscribe(self, user):
        if self.is_subscribed(user):
            self.subscribers = [u.id for u in self.subscribers if u != user.id]
            self.log_message(
                "%s(%s) has been unsubscribed" %
                ((" ".join([user.first_name, user.last_name]), user.username)),
                to_save=False)
            self.save()

    def is_owner(self, user):
        return self.owner == user

    def is_subscribed(self, user):
        return user.id in self.subscribers

    @property
    def is_unassigned(self):
        return self.owner is None

    @property
    def duration(self):
        dt = datetime.datetime.now() - self.timestamp
        return dt.days * 86400 + dt.seconds

    @property
    def display_duration(self):
        duration = datetime.datetime.now() - self.timestamp
        secs = duration.seconds % 60
        mins = (duration.seconds / 60) % 60
        hours = (duration.seconds / 3600) % 24
        days = duration.days
        r = "%02d:%02d:%02d" % (hours, mins, secs)
        if days:
            r = "%dd %s" % (days, r)
        return r

    @property
    def effective_style(self):
        if self.custom_style:
            return self.custom_style
        else:
            return AlarmSeverity.get_severity(self.severity).style

    def set_root(self, root_alarm):
        """
        Set root cause
        """
        if self.root:
            return
        if self.id == root_alarm.id:
            raise Exception("Cannot set self as root cause")
        # Detect loop
        root = root_alarm
        while root and root.root:
            root = root.root
            if root == self.id:
                return
            root = get_alarm(root)
        # Set root
        self.root = root_alarm.id
        self.log_message("Alarm %s has been marked as root cause" %
                         root_alarm.id)
        # self.save()  Saved by log_message
        root_alarm.log_message("Alarm %s has been marked as child" % self.id)
        self._change_root_severity()
        # Clear pending notifications
        Notification.purge_delayed("alarm:%s" % self.id)

    @classmethod
    def enable_caching(cls, ttl=600):
        cls._fields["alarm_class"].set_cache(ttl)
예제 #27
0
class AlarmClass(nosql.Document):
    """
    Alarm class
    """
    meta = {
        "collection": "noc.alarmclasses",
        "allow_inheritance": False,
        "json_collection": "fm.alarmclasses",
        "json_depends_on": ["fm.alarmseverities"]
    }

    name = fields.StringField(required=True, unique=True)
    uuid = fields.UUIDField(binary=True)
    description = fields.StringField(required=False)
    # Create or not create separate Alarm
    # if is_unique is True and there is active alarm
    # Do not create separate alarm if is_unique set
    is_unique = fields.BooleanField(default=False)
    # List of var names to be used as discriminator key
    discriminator = fields.ListField(nosql.StringField())
    # Can alarm status be cleared by user
    user_clearable = fields.BooleanField(default=True)
    # Default alarm severity
    default_severity = nosql.PlainReferenceField(AlarmSeverity)
    #
    datasources = fields.ListField(fields.EmbeddedDocumentField(DataSource))
    vars = fields.ListField(fields.EmbeddedDocumentField(AlarmClassVar))
    # Text messages
    subject_template = fields.StringField()
    body_template = fields.StringField()
    symptoms = fields.StringField()
    probable_causes = fields.StringField()
    recommended_actions = fields.StringField()

    # Flap detection
    flap_condition = fields.StringField(required=False,
                                        choices=[("none", "none"),
                                                 ("count", "count")],
                                        default=None)
    flap_window = fields.IntField(required=False, default=0)
    flap_threshold = fields.FloatField(required=False, default=0)
    # RCA
    root_cause = fields.ListField(
        fields.EmbeddedDocumentField(AlarmRootCauseCondition))
    # Job descriptions
    jobs = fields.ListField(fields.EmbeddedDocumentField(AlarmClassJob))
    #
    handlers = fields.ListField(fields.StringField())
    # Plugin settings
    plugins = fields.ListField(fields.EmbeddedDocumentField(AlarmPlugin))
    # Time in seconds to delay alarm risen notification
    notification_delay = fields.IntField(required=False)
    # Control time to reopen alarm instead of creating new
    control_time0 = fields.IntField(required=False)
    # Control time to reopen alarm after 1 reopen
    control_time1 = fields.IntField(required=False)
    # Control time to reopen alarm after >1 reopen
    control_timeN = fields.IntField(required=False)
    #
    category = nosql.ObjectIdField()

    def __unicode__(self):
        return self.name

    def save(self, *args, **kwargs):
        c_name = " | ".join(self.name.split(" | ")[:-1])
        c = AlarmClassCategory.objects.filter(name=c_name).first()
        if not c:
            c = AlarmClassCategory(name=c_name)
            c.save()
        self.category = c.id
        super(AlarmClass, self).save(*args, **kwargs)

    def get_discriminator(self, vars):
        """
        Calculate discriminator hash

        :param vars: Dict of vars
        :returns: Discriminator hash
        """
        if vars:
            ds = sorted(str(vars[n]) for n in self.discriminator)
            return hashlib.sha1("\x00".join(ds)).hexdigest()
        else:
            return hashlib.sha1("").hexdigest()

    def to_json(self):
        c = self
        r = ["{"]
        r += ["    \"name\": \"%s\"," % q(c.name)]
        r += ["    \"$collection\": \"%s\"," % self._meta["json_collection"]]
        r += ["    \"uuid\": \"%s\"," % c.uuid]
        if c.description:
            r += ["    \"desciption\": \"%s\"," % q(c.description)]
        r += ["    \"is_unique\": %s," % q(c.is_unique)]
        if c.is_unique and c.discriminator:
            r += [
                "    \"discriminator\": [%s]," %
                ", ".join(["\"%s\"" % q(d) for d in c.discriminator])
            ]
        r += ["    \"user_clearable\": %s," % q(c.user_clearable)]
        r += [
            "    \"default_severity__name\": \"%s\"," %
            q(c.default_severity.name)
        ]
        # datasources
        if c.datasources:
            r += ["    \"datasources\": ["]
            jds = []
            for ds in c.datasources:
                x = []
                x += ["            \"name\": \"%s\"" % q(ds.name)]
                x += ["            \"datasource\": \"%s\"" % q(ds.datasource)]
                ss = []
                for k in sorted(ds.search):
                    ss += [
                        "                \"%s\": \"%s\"" %
                        (q(k), q(ds.search[k]))
                    ]
                x += [
                    "            \"search\": {\n%s\n            }" %
                    (",\n".join(ss))
                ]
                jds += ["        {\n%s\n        }" % ",\n".join(x)]
            r += [",\n\n".join(jds)]
            r += ["    ],"]
        # vars
        vars = []
        for v in c.vars:
            vd = ["        {"]
            vd += ["            \"name\": \"%s\"," % q(v.name)]
            vd += ["            \"description\": \"%s\"" % q(v.description)]
            if v.default:
                vd[-1] += ","
                vd += ["            \"default\": \"%s\"" % q(v.default)]
            vd += ["        }"]
            vars += ["\n".join(vd)]
        r += ["    \"vars\": ["]
        r += [",\n".join(vars)]
        r += ["    ],"]
        # Handlers
        if self.handlers:
            hh = ["        \"%s\"" % h for h in self.handlers]
            r += ["    \"handlers\": ["]
            r += [",\n\n".join(hh)]
            r += ["    ],"]
        # Text
        r += ["    \"subject_template\": \"%s\"," % q(c.subject_template)]
        r += ["    \"body_template\": \"%s\"," % q(c.body_template)]
        r += ["    \"symptoms\": \"%s\"," % q(c.symptoms)]
        r += ["    \"probable_causes\": \"%s\"," % q(c.probable_causes)]
        r += [
            "    \"recommended_actions\": \"%s\"," % q(c.recommended_actions)
        ]
        # Root cause
        if self.root_cause:
            rc = []
            for rr in self.root_cause:
                rcd = ["        {"]
                rcd += ["            \"name\": \"%s\"," % rr.name]
                rcd += ["            \"root__name\": \"%s\"," % rr.root.name]
                rcd += ["            \"window\": %d," % rr.window]
                if rr.condition:
                    rcd += [
                        "            \"condition\": \"%s\"," % rr.condition
                    ]
                rcd += ["            \"match_condition\": {"]
                mcv = []
                for v in rr.match_condition:
                    mcv += [
                        "                \"%s\": \"%s\"" %
                        (v, rr.match_condition[v])
                    ]
                rcd += [",\n".join(mcv)]
                rcd += ["            }"]
                rcd += ["        }"]
                rc += ["\n".join(rcd)]
            if r[-1][-1] != ",":
                r[-1] += ","
            r += ["    \"root_cause\": ["]
            r += [",\n".join(rc)]
            r += ["    ]"]
        # Jobs
        if self.jobs:
            jobs = []
            for job in self.jobs:
                jd = ["        {"]
                jd += ["            \"job\": \"%s\"," % job.job]
                jd += ["            \"interval\": %d," % job.interval]
                jd += ["            \"vars\": {"]
                jv = []
                for v in job.vars:
                    jv += ["                \"%s\": \"%s\"" % (v, job.vars[v])]
                jd += [",\n".join(jv)]
                jd += ["            }"]
                jd += ["        }"]
                jobs += ["\n".join(jd)]
            if r[-1][-1] != ",":
                r[-1] += ","
            r += ["    \"jobs\": ["]
            r += [",\n".join(jobs)]
            r += ["    ]"]
        # Plugins
        if self.plugins:
            if r[-1][-1] != ",":
                r[-1] += ","
            plugins = []
            for p in self.plugins:
                pd = ["        {"]
                pd += ["            \"name\": \"%s\"" % p.name]
                if p.config:
                    pd[-1] += ","
                    pc = []
                    for v in p.config:
                        pc += [
                            "                \"%s\": \"%s\"" %
                            (v, p.config.vars[v])
                        ]
                    pd += ["            \"config\": {"]
                    pd += [",\n".join(pc)]
                    pd += ["            }"]
                pd += ["        }"]
                plugins += ["\n".join(pd)]
            r += ["    \"plugins\": ["]
            r += [",\n".join(plugins)]
            r += ["    ]"]
        if self.notification_delay:
            if r[-1][-1] != ",":
                r[-1] += ","
            r += ["    \"notification_delay\": %d" % self.notification_delay]
        if self.control_time0:
            if r[-1][-1] != ",":
                r[-1] += ","
            r += ["    \"control_time0\": %d" % self.control_time0]
            if self.control_time1:
                r[-1] += ","
                r += ["    \"control_time1\": %d" % self.control_time1]
                if self.control_timeN:
                    r[-1] += ","
                    r += ["    \"control_timeN\": %d" % self.control_timeN]
        # Close
        if r[-1].endswith(","):
            r[-1] = r[-1][:-1]
        r += ["}", ""]
        return "\n".join(r)

    def get_json_path(self):
        p = [quote_safe_path(n.strip()) for n in self.name.split("|")]
        return os.path.join(*p) + ".json"

    @property
    def config(self):
        if not hasattr(self, "_config"):
            self._config = AlarmClassConfig.objects.filter(
                alarm_class=self.id).first()
        return self._config

    def get_notification_delay(self):
        if self.config:
            return self.config.notification_delay or None
        else:
            return self.notification_delay or None

    def get_control_time(self, reopens):
        if reopens == 0:
            if self.config:
                return self.config.control_time0 or None
            else:
                return self.control_time0 or None
        elif reopens == 1:
            if self.config:
                return self.config.control_time1 or None
            else:
                return self.control_time1 or None
        else:
            if self.config:
                return self.config.control_timeN or None
            else:
                return self.control_timeN or None
예제 #28
0
파일: alarmclass.py 프로젝트: skripkar/noc
class AlarmClass(nosql.Document):
    """
    Alarm class
    """
    meta = {
        "collection": "noc.alarmclasses",
        "strict": False,
        "auto_create_index": False,
        "json_collection": "fm.alarmclasses",
        "json_depends_on": [
            "fm.alarmseverities"
        ],
    }

    name = fields.StringField(required=True, unique=True)
    uuid = fields.UUIDField(binary=True)
    description = fields.StringField(required=False)
    # Create or not create separate Alarm
    # if is_unique is True and there is active alarm
    # Do not create separate alarm if is_unique set
    is_unique = fields.BooleanField(default=False)
    # List of var names to be used as discriminator key
    discriminator = fields.ListField(nosql.StringField())
    # Can alarm status be cleared by user
    user_clearable = fields.BooleanField(default=True)
    # Default alarm severity
    default_severity = nosql.PlainReferenceField(AlarmSeverity)
    #
    datasources = fields.ListField(fields.EmbeddedDocumentField(DataSource))
    vars = fields.ListField(fields.EmbeddedDocumentField(AlarmClassVar))
    # Text messages
    subject_template = fields.StringField()
    body_template = fields.StringField()
    symptoms = fields.StringField()
    probable_causes = fields.StringField()
    recommended_actions = fields.StringField()

    # Flap detection
    flap_condition = fields.StringField(
        required=False,
        choices=[("none", "none"), ("count", "count")],
        default="none")
    flap_window = fields.IntField(required=False, default=0)
    flap_threshold = fields.FloatField(required=False, default=0)
    # RCA
    root_cause = fields.ListField(
        fields.EmbeddedDocumentField(AlarmRootCauseCondition))
    topology_rca = fields.BooleanField(default=False)
    # List of handlers to be called on alarm raising
    handlers = fields.ListField(fields.StringField())
    # List of handlers to be called on alarm clear
    clear_handlers = fields.ListField(fields.StringField())
    # Plugin settings
    plugins = fields.ListField(fields.EmbeddedDocumentField(AlarmPlugin))
    # Time in seconds to delay alarm risen notification
    notification_delay = fields.IntField(required=False)
    # Control time to reopen alarm instead of creating new
    control_time0 = fields.IntField(required=False)
    # Control time to reopen alarm after 1 reopen
    control_time1 = fields.IntField(required=False)
    # Control time to reopen alarm after >1 reopen
    control_timeN = fields.IntField(required=False)
    # Consequence recover time
    # Root cause will be detached if consequence alarm
    # will not clear itself in *recover_time*
    recover_time = fields.IntField(required=False, default=300)
    #
    bi_id = fields.LongField(unique=True)
    #
    category = nosql.ObjectIdField()

    _id_cache = cachetools.TTLCache(maxsize=1000, ttl=60)
    _bi_id_cache = cachetools.TTLCache(maxsize=1000, ttl=60)
    _name_cache = cachetools.TTLCache(maxsize=1000, ttl=60)

    _handlers_cache = {}
    _clear_handlers_cache = {}

    def __unicode__(self):
        return self.name

    @classmethod
    @cachetools.cachedmethod(operator.attrgetter("_id_cache"), lock=lambda _: id_lock)
    def get_by_id(cls, id):
        return AlarmClass.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 AlarmClass.objects.filter(bi_id=id).first()

    @classmethod
    @cachetools.cachedmethod(operator.attrgetter("_name_cache"), lock=lambda _: id_lock)
    def get_by_name(cls, name):
        return AlarmClass.objects.filter(name=name).first()

    def get_handlers(self):
        @cachetools.cached(self._handlers_cache, key=lambda x: x.id, lock=handlers_lock)
        def _get_handlers(alarm_class):
            handlers = []
            for hh in alarm_class.handlers:
                try:
                    h = get_handler(hh)
                except ImportError:
                    h = None
                if h:
                    handlers += [h]
            return handlers

        return _get_handlers(self)

    def get_clear_handlers(self):
        @cachetools.cached(self._clear_handlers_cache, key=lambda x: x.id, lock=handlers_lock)
        def _get_handlers(alarm_class):
            handlers = []
            for hh in alarm_class.clear_handlers:
                try:
                    h = get_handler(hh)
                except ImportError:
                    h = None
                if h:
                    handlers += [h]
            return handlers

        return _get_handlers(self)

    def save(self, *args, **kwargs):
        c_name = " | ".join(self.name.split(" | ")[:-1])
        c = AlarmClassCategory.objects.filter(name=c_name).first()
        if not c:
            c = AlarmClassCategory(name=c_name)
            c.save()
        self.category = c.id
        super(AlarmClass, self).save(*args, **kwargs)

    def get_discriminator(self, vars):
        """
        Calculate discriminator hash

        :param vars: Dict of vars
        :returns: Discriminator hash
        """
        if vars:
            ds = sorted(str(vars[n]) for n in self.discriminator)
            return hashlib.sha1("\x00".join(ds)).hexdigest()
        else:
            return hashlib.sha1("").hexdigest()

    def to_json(self):
        c = self
        r = ["{"]
        r += ["    \"name\": \"%s\"," % q(c.name)]
        r += ["    \"$collection\": \"%s\"," % self._meta["json_collection"]]
        r += ["    \"uuid\": \"%s\"," % c.uuid]
        if c.description:
            r += ["    \"desciption\": \"%s\"," % q(c.description)]
        r += ["    \"is_unique\": %s," % q(c.is_unique)]
        if c.is_unique and c.discriminator:
            r += ["    \"discriminator\": [%s]," % ", ".join(["\"%s\"" % q(d) for d in c.discriminator])]
        r += ["    \"user_clearable\": %s," % q(c.user_clearable)]
        r += ["    \"default_severity__name\": \"%s\"," % q(c.default_severity.name)]
        # datasources
        if c.datasources:
            r += ["    \"datasources\": ["]
            jds = []
            for ds in c.datasources:
                x = []
                x += ["            \"name\": \"%s\"" % q(ds.name)]
                x += ["            \"datasource\": \"%s\"" % q(ds.datasource)]
                ss = []
                for k in sorted(ds.search):
                    ss += ["                \"%s\": \"%s\"" % (q(k), q(ds.search[k]))]
                x += ["            \"search\": {\n%s\n            }" % (",\n".join(ss))]
                jds += ["        {\n%s\n        }" % ",\n".join(x)]
            r += [",\n\n".join(jds)]
            r += ["    ],"]
        # vars
        vars = []
        for v in c.vars:
            vd = ["        {"]
            vd += ["            \"name\": \"%s\"," % q(v.name)]
            vd += ["            \"description\": \"%s\"" % q(v.description)]
            if v.default:
                vd[-1] += ","
                vd += ["            \"default\": \"%s\"" % q(v.default)]
            vd += ["        }"]
            vars += ["\n".join(vd)]
        r += ["    \"vars\": ["]
        r += [",\n".join(vars)]
        r += ["    ],"]
        # Handlers
        if self.handlers:
            hh = ["        \"%s\"" % h for h in self.handlers]
            r += ["    \"handlers\": ["]
            r += [",\n\n".join(hh)]
            r += ["    ],"]
        if self.clear_handlers:
            hh = ["        \"%s\"" % h for h in self.clear_handlers]
            r += ["    \"clear_handlers\": ["]
            r += [",\n\n".join(hh)]
            r += ["    ],"]
        # Text
        r += ["    \"subject_template\": \"%s\"," % q(c.subject_template)]
        r += ["    \"body_template\": \"%s\"," % q(c.body_template)]
        r += ["    \"symptoms\": \"%s\"," % q(c.symptoms if c.symptoms else "")]
        r += ["    \"probable_causes\": \"%s\"," % q(c.probable_causes if c.probable_causes else "")]
        r += ["    \"recommended_actions\": \"%s\"," % q(c.recommended_actions if c.recommended_actions else "")]
        # Root cause
        if self.root_cause:
            rc = []
            for rr in self.root_cause:
                rcd = ["        {"]
                rcd += ["            \"name\": \"%s\"," % rr.name]
                rcd += ["            \"root__name\": \"%s\"," % rr.root.name]
                rcd += ["            \"window\": %d," % rr.window]
                if rr.condition:
                    rcd += ["            \"condition\": \"%s\"," % rr.condition]
                rcd += ["            \"match_condition\": {"]
                mcv = []
                for v in rr.match_condition:
                    mcv += ["                \"%s\": \"%s\"" % (v, rr.match_condition[v])]
                rcd += [",\n".join(mcv)]
                rcd += ["            }"]
                rcd += ["        }"]
                rc += ["\n".join(rcd)]
            if r[-1][-1] != ",":
                r[-1] += ","
            r += ["    \"root_cause\": ["]
            r += [",\n".join(rc)]
            r += ["    ]"]
        if self.topology_rca:
            if r[-1][-1] != ",":
                r[-1] += ","
            r += ["    \"topology_rca\": true"]
        # Plugins
        if self.plugins:
            if r[-1][-1] != ",":
                r[-1] += ","
            plugins = []
            for p in self.plugins:
                pd = ["        {"]
                pd += ["            \"name\": \"%s\"" % p.name]
                if p.config:
                    pd[-1] += ","
                    pc = []
                    for v in p.config:
                        pc += ["                \"%s\": \"%s\"" % (v, p.config.vars[v])]
                    pd += ["            \"config\": {"]
                    pd += [",\n".join(pc)]
                    pd += ["            }"]
                pd += ["        }"]
                plugins += ["\n".join(pd)]
            r += ["    \"plugins\": ["]
            r += [",\n".join(plugins)]
            r += ["    ]"]
        if self.notification_delay:
            if r[-1][-1] != ",":
                r[-1] += ","
            r += ["    \"notification_delay\": %d" % self.notification_delay]
        if self.control_time0:
            if r[-1][-1] != ",":
                r[-1] += ","
            r += ["    \"control_time0\": %d" % self.control_time0]
            if self.control_time1:
                r[-1] += ","
                r += ["    \"control_time1\": %d" % self.control_time1]
                if self.control_timeN:
                    r[-1] += ","
                    r += ["    \"control_timeN\": %d" % self.control_timeN]
        if self.recover_time:
            if r[-1][-1] != ",":
                r[-1] += ","
            r += ["    \"recover_time\": %d" % self.recover_time]
        # Close
        if r[-1].endswith(","):
            r[-1] = r[-1][:-1]
        r += ["}", ""]
        return "\n".join(r)

    def get_json_path(self):
        p = [quote_safe_path(n.strip()) for n in self.name.split("|")]
        return os.path.join(*p) + ".json"

    @property
    def config(self):
        if not hasattr(self, "_config"):
            self._config = AlarmClassConfig.objects.filter(alarm_class=self.id).first()
        return self._config

    def get_notification_delay(self):
        if self.config:
            return self.config.notification_delay or None
        else:
            return self.notification_delay or None

    def get_control_time(self, reopens):
        if reopens == 0:
            if self.config:
                return self.config.control_time0 or None
            else:
                return self.control_time0 or None
        elif reopens == 1:
            if self.config:
                return self.config.control_time1 or None
            else:
                return self.control_time1 or None
        else:
            if self.config:
                return self.config.control_timeN or None
            else:
                return self.control_timeN or None
예제 #29
0
class ArchivedAlarm(nosql.Document):
    meta = {
        "collection": "noc.alarms.archived",
        "allow_inheritance": False,
        "indexes": [
            "root",
            "control_time",
            "timestamp",
            "managed_object"
        ]
    }
    status = "C"

    timestamp = nosql.DateTimeField(required=True)
    clear_timestamp = nosql.DateTimeField(required=True)
    managed_object = nosql.ForeignKeyField(ManagedObject)
    alarm_class = nosql.PlainReferenceField(AlarmClass)
    severity = nosql.IntField(required=True)
    vars = nosql.DictField()
    log = nosql.ListField(nosql.EmbeddedDocumentField(AlarmLog))
    #
    opening_event = nosql.ObjectIdField(required=False)
    closing_event = nosql.ObjectIdField(required=False)
    # Number of reopens
    reopens = nosql.IntField(required=False)
    # Copied discriminator
    discriminator = nosql.StringField(required=False)
    # Control time within alarm will be reopen instead
    # instead of creating the new alarm
    control_time = nosql.DateTimeField(required=False)
    # RCA
    # Reference to root cause (Active Alarm or Archived Alarm instance)
    root = nosql.ObjectIdField(required=False)

    def __unicode__(self):
        return u"%s" % self.id

    def log_message(self, message):
        self.log += [AlarmLog(timestamp=datetime.datetime.now(),
                     from_status=self.status, to_status=self.status,
                     message=message)]
        self.save()

    def get_template_vars(self):
        """
        Prepare template variables
        """
        vars = self.vars.copy()
        vars.update({"event": self})
        return vars

    @property
    def subject(self):
        ctx = Context(self.get_template_vars())
        s = Template(self.alarm_class.subject_template).render(ctx)
        if len(s) >= 255:
            s = s[:125] + " ... " + s[-125:]
        return s

    @property
    def body(self):
        ctx = Context(self.get_template_vars())
        s = Template(self.alarm_class.body_template).render(ctx)
        return s

    @property
    def duration(self):
        dt = self.clear_timestamp - self.timestamp
        return dt.days * 86400 + dt.seconds

    @property
    def display_duration(self):
        duration = self.clear_timestamp - self.timestamp
        secs = duration.seconds % 60
        mins = (duration.seconds / 60) % 60
        hours = (duration.seconds / 3600) % 24
        days = duration.days
        if days:
            return "%dd %02d:%02d:%02d" % (days, hours, mins, secs)
        else:
            return "%02d:%02d:%02d" % (hours, mins, secs)

    @property
    def effective_style(self):
        return AlarmSeverity.get_severity(self.severity).style

    def set_root(self, root_alarm):
        pass

    def reopen(self, message):
        """
        Reopen alarm back
        """
        reopens = self.reopens or 0
        ts = datetime.datetime.now()
        log = self.log + [AlarmLog(timestamp=ts, from_status="C",
                                   to_status="A", message=message)]
        a = ActiveAlarm(
            id=self.id,
            timestamp=self.timestamp,
            last_update=ts,
            managed_object=self.managed_object,
            alarm_class=self.alarm_class,
            severity=self.severity,
            vars=self.vars,
            log=log,
            root=self.root,
            opening_event=self.opening_event,
            discriminator=self.discriminator,
            reopens=reopens + 1
        )
        a.save()
        # @todo: Clear related correlator jobs
        self.delete()
        # Remove pending control_notify job
        remove_job("fm.correlator", "control_notify", key=a.id)
        # Send notifications
        # Do not set notifications for child and for previously reopened
        # alarms
        if not a.root and not reopens:
            a.managed_object.event(a.managed_object.EV_ALARM_REOPENED, {
                "alarm": a,
                "subject": a.subject,
                "body": a.body,
                "symptoms": a.alarm_class.symptoms,
                "recommended_actions": a.alarm_class.recommended_actions,
                "probable_causes": a.alarm_class.probable_causes
            })
        return a
예제 #30
0
파일: mib.py 프로젝트: fossabot/noc
class MIB(nosql.Document):
    meta = {"collection": "noc.mibs", "allow_inheritance": False}
    name = nosql.StringField(required=True, unique=True)
    description = nosql.StringField(required=False)
    last_updated = nosql.DateTimeField(required=True)
    depends_on = nosql.ListField(nosql.StringField())
    # TC definitions: name -> SYNTAX
    typedefs = nosql.DictField(required=False)
    # Compiled MIB version
    version = nosql.IntField(required=False, default=0)

    MIBRequiredException = MIBRequiredException

    def __unicode__(self):
        return self.name

    def get_text(self):
        """
        Returns MIB text
        :return:
        """
        for d in ["local/share/mibs", "share/mibs"]:
            path = os.path.join(d, self.name + ".mib")
            if os.path.isfile(path):
                with open(path) as f:
                    return f.read()
        return ""

    @classmethod
    def parse_syntax(cls, syntax):
        """
        Process part of smidump output and convert to syntax structure
        """
        if "base_type" in syntax:
            # Already compiled
            return syntax
        s = {}
        if "basetype" in syntax:
            s["base_type"] = syntax["basetype"]
        elif "base_type" in syntax:
            s["base_type"] = syntax["base_type"]
        if "name" in syntax and "module" in syntax:
            if syntax["module"] == "":
                # Empty module -> builitin types
                s["base_type"] = syntax["name"]
            else:
                # Resolve references
                mib = MIB.objects.filter(name=syntax["module"]).first()
                if mib is None:
                    raise MIBNotFoundException(syntax["module"])
                if not mib.typedefs or syntax["name"] not in mib.typedefs:
                    return {}
                td = mib.typedefs[syntax["name"]]
                for k in ["base_type", "display_hint", "enum_map"]:
                    if k in td:
                        s[k] = td[k]
        if s["base_type"] in ("Enumeration", "Bits"):
            enum_map = s.get("enum_map", {})
            for k in syntax:
                sk = syntax[k]
                if type(sk) != dict:
                    continue
                if "nodetype" in sk and sk["nodetype"] == "namednumber":
                    enum_map[sk["number"]] = k
            s["enum_map"] = enum_map
        if "format" in syntax:
            s["display_hint"] = syntax["format"]
        return s

    @classmethod
    def load(cls, path, force=False):
        """
        Load MIB from file
        :param path: MIB path
        :param force: Load anyways
        :return: MIB object
        """
        if not os.path.exists(path):
            raise ValueError("File not found: %s" % path)
        # Build SMIPATH variable for smidump
        # to exclude locally installed MIBs
        smipath = ["share/mibs", "local/share/mibs"]
        # Pass MIB through smilint to detect missed modules
        f = subprocess.Popen([config.get("path", "smilint"), "-m", path],
                             stderr=subprocess.PIPE,
                             env={
                                 "SMIPATH": ":".join(smipath)
                             }).stderr
        for l in f:
            match = rx_module_not_found.search(l.strip())
            if match:
                raise MIBRequiredException("Uploaded MIB", match.group(1))
        # Convert MIB to python module and load
        with temporary_file() as p:
            subprocess.check_call([
                config.get("path", "smidump"), "-k", "-q", "-f", "python",
                "-o", p, path
            ],
                                  env={"SMIPATH": ":".join(smipath)})
            # Add coding string
            with open(p) as f:
                data = unicode(f.read(), "ascii", "ignore").encode("ascii")
            with open(p, "w") as f:
                f.write(data)
            m = imp.load_source("mib", p)
        mib_name = m.MIB["moduleName"]
        # Check module dependencies
        depends_on = {}  # MIB Name -> Object ID
        if "imports" in m.MIB:
            for i in m.MIB["imports"]:
                if "module" not in i:
                    continue
                rm = i["module"]
                if rm in depends_on:
                    continue
                md = MIB.objects.filter(name=rm).first()
                if md is None:
                    raise MIBRequiredException(mib_name, rm)
                depends_on[rm] = md
        # Get MIB latest revision date
        try:
            last_updated = datetime.datetime.strptime(
                sorted([x["date"] for x in m.MIB[mib_name]["revisions"]])[-1],
                "%Y-%m-%d %H:%M")
        except:
            last_updated = datetime.datetime(year=1970, month=1, day=1)
        # Extract MIB typedefs
        typedefs = {}
        if "typedefs" in m.MIB:
            for t in m.MIB["typedefs"]:
                typedefs[t] = cls.parse_syntax(m.MIB["typedefs"][t])
        # Check mib already uploaded
        mib_description = m.MIB[mib_name].get("description", None)
        mib = MIB.objects.filter(name=mib_name).first()
        if force and mib:
            # Delete mib to forceful update
            MIBData.objects.filter(mib=mib.id).delete()
            mib.clean()
            mib.delete()
            mib = None
        if mib is not None:
            # Skip same version
            if mib.last_updated >= last_updated:
                return mib
            mib.description = mib_description
            mib.last_updated = last_updated
            mib.depends_on = sorted(depends_on)
            mib.typedefs = typedefs
            mib.save()
            # Delete all MIB Data
            mib.clean()
        else:
            # Create MIB
            mib = MIB(name=mib_name,
                      description=mib_description,
                      last_updated=last_updated,
                      depends_on=sorted(depends_on),
                      typedefs=typedefs)
            mib.save()
        # Upload MIB data
        data = []
        for i in ["nodes", "notifications"]:
            if i in m.MIB:
                data += [{
                    "name":
                    "%s::%s" % (mib_name, node),
                    "oid":
                    v["oid"],
                    "description":
                    v.get("description"),
                    "syntax":
                    v["syntax"]["type"] if "syntax" in v else None
                } for node, v in m.MIB[i].items()]
        mib.load_data(data)
        # Save MIB to cache if not uploaded from cache
        lcd = os.path.join("local", "share", "mibs")
        if not os.path.isdir(lcd):  # Ensure directory exists
            os.makedirs(os.path.join("local", "share", "mibs"))
        local_cache_path = os.path.join(lcd, "%s.mib" % mib_name)
        cache_path = os.path.join("share", "mibs", "%s.mib" % mib_name)
        if ((os.path.exists(local_cache_path)
             and os.path.samefile(path, local_cache_path))
                or (os.path.exists(cache_path)
                    and os.path.samefile(path, cache_path))):
            return mib
        with open(path) as f:
            data = f.read()
        safe_rewrite(local_cache_path, data)
        return mib

    def load_data(self, data):
        """
        Load mib data from list of {oid:, name:, description:, syntax:}
        :param data:
        :return:
        """
        # Get MIB preference
        mp = MIBPreference.objects.filter(mib=self.name).first()
        mib_preference = mp.preference if mp else None
        prefs = {}  # MIB Preferences cache
        # Load data
        for v in data:
            oid = v["oid"]
            oid_name = v["name"]
            description = v.get("description", None)
            o = MIBData.objects.filter(oid=oid).first()
            if o is not None:
                if o.name == oid_name:
                    # Same oid, same name: duplicated declaration.
                    # Silently skip
                    continue
                # For same MIB - leave first entry
                if oid_name.split("::", 1)[0] == o.name.split("::", 1)[0]:
                    continue
                # Try to resolve collision
                if not mib_preference:
                    # No preference for target MIB
                    raise OIDCollision(oid, oid_name, o.name,
                                       "No preference for %s" % self.name)
                o_mib = o.name.split("::")[0]
                if o_mib not in prefs:
                    mp = MIBPreference.objects.filter(mib=o_mib).first()
                    if not mp:
                        # No preference for destination MIB
                        raise OIDCollision(oid, oid_name, o.name,
                                           "No preference for %s" % o_mib)
                    prefs[o_mib] = mp.preference  # Add to cache
                o_preference = prefs[o_mib]
                if mib_preference == o_preference:
                    # Equal preferences, collision
                    raise OIDCollision(oid, oid_name, o.name,
                                       "Equal preferences")
                if mib_preference < o_preference:
                    # Replace existing
                    o.aliases = sorted(o.aliases + [o.name])
                    o.name = oid_name
                    o.mib = self.id
                    if description:
                        o.description = description
                    syntax = v.get("syntax")
                    if syntax:
                        o.syntax = MIB.parse_syntax(syntax)
                    o.save()
                else:
                    # Append to aliases
                    if oid_name not in o.aliases:
                        o.aliases = sorted(o.aliases + [oid_name])
                        o.save()
            else:
                # No OID collision found, save
                syntax = v.get("syntax")
                if syntax:
                    syntax = MIB.parse_syntax(syntax)
                MIBData(mib=self.id,
                        oid=oid,
                        name=oid_name,
                        description=description,
                        syntax=syntax).save()

    @classmethod
    def get_oid(cls, name):
        """
        Get OID by name
        """
        tail = ""
        match = rx_tailing_numbers.match(name)
        if match:
            name, tail = match.groups()
        # Search by primary name
        d = MIBData.objects.filter(name=name).first()
        if not d:
            # Search by aliases
            d = MIBData.objects.filter(aliases=name).first()
        if d:
            return d.oid + tail
        return None

    @classmethod
    def get_name(cls, oid):
        """
        Get longest match name by OID
        """
        oid = OIDAlias.rewrite(oid)
        l_oid = oid.split(".")
        rest = []
        while l_oid:
            c_oid = ".".join(l_oid)
            d = MIBData.objects.filter(oid=c_oid).first()
            if d:
                return MIBAlias.rewrite(".".join([d.name] + rest))
            else:
                rest = [l_oid.pop()] + rest
        return oid

    @classmethod
    def get_name_and_syntax(cls, oid):
        """
        :return: (name, syntax)
        """
        oid = OIDAlias.rewrite(oid)
        l_oid = oid.split(".")
        rest = []
        while l_oid:
            c_oid = ".".join(l_oid)
            d = MIBData.objects.filter(oid=c_oid).first()
            if d:
                name = d.name
                if rest:
                    name += "." + ".".join(reversed(rest))
                return (MIBAlias.rewrite(name),
                        SyntaxAlias.rewrite(name, d.syntax))
            else:
                rest += [l_oid.pop()]
        return oid, None

    @classmethod
    def get_description(cls, name):
        """
        Get longest match description by name
        """
        match = rx_tailing_numbers.match(name)
        if match:
            name, _ = match.groups()
        # Search by primary name
        d = MIBData.objects.filter(name=name).first()
        if not d:
            # Search by aliases
            d = MIBData.objects.filter(aliases=name).first()
        if d:
            return d.description
        else:
            return None

    @property
    def depended_by(self):
        return MIB.objects.filter(depends_on=self.name)

    def clean(self):
        """
        Gracefully wipe out MIB data
        """
        # Delete data without aliases
        MIBData.objects.filter(mib=self.id, aliases=[]).delete()
        # Dereference aliases
        prefs = {}  # MIB -> Preference
        for o in MIBData.objects.filter(mib=self.id, aliases__ne=[]):
            if not o.aliases:  # NO aliases
                o.delete()
                continue
            if len(o.aliases) == 1:  # Only one alias
                ba = o.aliases[0]
            else:
                # Find preferable alias
                ba = None
                lp = None
                for a in o.aliases:
                    am = a.split("::")[0]
                    # Find MIB preference
                    if am not in prefs:
                        p = MIBPreference(mib=am).first()
                        if p is None:
                            raise Exception("No preference for %s" % am)
                        prefs[am] = p.preference
                    p = prefs[am]
                    if lp is None or p < lp:
                        # Better
                        ba = a
                        lp = p
            # Promote preferable alias
            o.name = ba
            o.aliases = [a for a in o.aliases if a != ba]
            o.save()

    @classmethod
    def resolve_vars(cls, vars):
        """
        Resolve FM key -> value dict according to MIBs

        :param cls:
        :param vars:
        :return:
        """
        r = {}
        for k in vars:
            if not is_oid(k):
                # Nothing to resolve
                continue
            v = fm_unescape(vars[k])
            rk, syntax = cls.get_name_and_syntax(k)
            rv = v
            if syntax:
                # Format value according to syntax
                if syntax["base_type"] == "Enumeration":
                    # Expand enumerated type
                    try:
                        rv = syntax["enum_map"][str(v)]
                    except KeyError:
                        pass
                elif syntax["base_type"] == "Bits":
                    # @todo: Fix ugly hack
                    if v.startswith("="):
                        xv = int(v[1:], 16)
                    else:
                        xv = 0
                        for c in v:
                            xv = (xv << 8) + ord(c)
                    # Decode
                    b_map = syntax.get("enum_map", {})
                    b = []
                    n = 0
                    while xv:
                        if xv & 1:
                            x = str(n)
                            if x in b_map:
                                b = [b_map[x]] + b
                            else:
                                b = ["%X" % (1 << n)]
                        n += 1
                        xv >>= 1
                    rv = "(%s)" % ",".join(b)
                else:
                    # Render according to TC
                    rv = render_tc(v, syntax["base_type"],
                                   syntax.get("display_hint", None))
                    try:
                        unicode(rv, "utf8")
                    except:
                        # Escape invalid UTF8
                        rv = fm_escape(rv)
            else:
                try:
                    unicode(rv, "utf8")
                except:
                    # escape invalid UTF8
                    rv = fm_escape(rv)
            if is_oid(v):
                # Resolve OID in value
                rv = MIB.get_name(v)
            if rk != k or rv != v:
                r[rk] = rv
        return r