Ejemplo n.º 1
0
class AccountNavlet(models.Model):
    """Store information about a users navlets"""
    navlet = VarcharField()
    order = models.IntegerField(default=0, db_column='displayorder')
    account = models.ForeignKey(Account, db_column='account')
    preferences = DictAsJsonField(null=True)
    column = models.IntegerField(db_column='col')
    dashboard = models.ForeignKey(AccountDashboard, related_name='widgets')

    def __unicode__(self):
        return "%s - %s" % (self.navlet, self.account)

    def to_json_dict(self):
        return {
            'navlet': self.navlet,
            'preferences': self.preferences,
            'column': self.column,
            'order': self.order,
        }

    class Meta(object):
        db_table = 'account_navlet'
        ordering = ['order']
Ejemplo n.º 2
0
Archivo: event.py Proyecto: Uninett/nav
class Acknowledgement(models.Model):
    """Alert acknowledgements"""

    alert = models.OneToOneField(
        'AlertHistory',
        on_delete=models.CASCADE,
        null=False,
        blank=False,
        primary_key=True,
    )
    account = models.ForeignKey('Account',
                                on_delete=models.CASCADE,
                                null=False,
                                blank=False)
    comment = VarcharField(blank=True)
    date = models.DateTimeField(null=False, default=dt.datetime.now)

    class Meta(object):
        db_table = 'alerthist_ack'

    def __str__(self):
        return u"%r acknowledged by %s at %s" % (self.alert, self.account,
                                                 self.date)
Ejemplo n.º 3
0
class LogEntry(models.Model):
    """
    Logs mostly user actions in NAV

    Example logentry:
    LogEntry.add_log_entry(
        account,           # actor
        u'set-ifalias',    # verb
        u'{actor}: {object} - ifalias set to "%s"' % ifalias,  # template
        subsystem=u'portadmin',                                # optional
        object=interface,                                      # optional
    )
    """
    actor_model = VarcharField()
    actor_pk = VarcharField()
    actor = LegacyGenericForeignKey('actor_model', 'actor_pk')

    object_model = VarcharField(null=True)
    object_pk = VarcharField(null=True)
    object = LegacyGenericForeignKey('object_model', 'object_pk')

    target_model = VarcharField(null=True)
    target_pk = VarcharField(null=True)
    target = LegacyGenericForeignKey('target_model', 'target_pk')

    timestamp = models.DateTimeField()

    verb = models.SlugField()

    summary = models.TextField()

    subsystem = VarcharField(blank=True, null=True)

    before = models.TextField(blank=True, null=True)
    after = models.TextField(blank=True, null=True)

    @classmethod
    def add_log_entry(cls,
                      actor,
                      verb,
                      template,
                      subsystem=None,
                      object=None,
                      target=None,
                      before=None,
                      after=None):
        """LogEntry factory"""
        self = cls()
        dict = {'actor': actor, 'object': object, 'target': target}
        for k, v in dict.items():
            dict[k] = getattr(v, 'audit_logname', u'%s' % v)
        try:
            self.summary = template.format(**dict)
        except KeyError as error:
            self.summary = 'Error creating summary - see error log'
            _logger.error('KeyError when creating summary: %s', error)
        self.verb = verb
        self.actor_model = find_modelname(actor)
        self.object_model = find_modelname(object) if object else None
        self.target_model = find_modelname(target) if target else None
        self.actor_pk = actor.pk
        self.object_pk = object.pk if object else None
        self.target_pk = target.pk if target else None
        self.timestamp = utcnow()
        self.subsystem = subsystem if subsystem else None
        self.before = force_text(before)
        self.after = force_text(after)
        self.save()
        return self

    @staticmethod
    def add_create_entry(actor, obj):
        """Add log entry for created objects

        :type actor: nav.models.profiles.Account
        """
        model = obj.__class__.__name__.lower()
        LogEntry.add_log_entry(actor,
                               u'create-{}'.format(model),
                               u'{actor} created {object}',
                               after=obj,
                               object=obj)

    @staticmethod
    def add_delete_entry(actor, obj, template=None):
        """Add log entry for deleted objects"""
        model = obj.__class__.__name__.lower()
        template = template or u'{actor} deleted {object}'
        LogEntry.add_log_entry(actor,
                               u'delete-{}'.format(model),
                               template,
                               before=obj,
                               object=obj)

    @staticmethod
    def add_edit_entry(actor, old, new, attribute, include_values=True):
        """Add log entry for edited objects

        :type attribute: str
        """
        def dict_to_string(d):
            """
            {"a": "b", "c": "d"} => "a=b, c=d"
            """
            return u", ".join(u"{}={}".format(x, y) for x, y in d.items())

        model = new.__class__.__name__.lower()
        prefix = u'{actor} edited {object}'
        old_value = getattr(old, attribute)
        new_value = getattr(new, attribute)
        if include_values:
            # Dicts f***s up the template, try to intervene
            if isinstance(old_value, dict):
                old_value = dict_to_string(old_value)
            if isinstance(new_value, dict):
                new_value = dict_to_string(new_value)
            summary = u"{} changed from '{}' to '{}'".format(
                attribute, old_value, new_value)
        else:
            summary = u"{} changed".format(attribute)

        LogEntry.add_log_entry(actor,
                               u'edit-{}-{}'.format(model, attribute),
                               u'{}: {}'.format(prefix, summary),
                               before=old,
                               after=new,
                               object=new)

    @staticmethod
    def compare_objects(actor,
                        old,
                        new,
                        attribute_list,
                        censored_attributes=None):
        """Checks for differences in two objects given an attribute-list

        :type actor: nav.models.profiles.Account
        :type old: models.Model
        :type new: models.Model
        :type attribute_list: list[str]

        Adds a log entry for each attribute where the two objects differ.
        """
        if censored_attributes is None:
            censored_attributes = []

        for attribute in attribute_list:
            old_value = getattr(old, attribute)
            new_value = getattr(new, attribute)
            if old_value != new_value:
                include_values = attribute not in censored_attributes
                LogEntry.add_edit_entry(actor,
                                        old,
                                        new,
                                        attribute,
                                        include_values=include_values)

    def __str__(self):
        return self.summary
Ejemplo n.º 4
0
class Service(models.Model):
    """From NAV Wiki: The service table defines the services on a netbox that
    serviceMon monitors."""

    UP_UP = 'y'
    UP_DOWN = 'n'
    UP_SHADOW = 's'
    UP_CHOICES = (
        (UP_UP, 'up'),
        (UP_DOWN, 'down'),
        (UP_SHADOW, 'shadow'),
    )
    TIME_FRAMES = ('day', 'week', 'month')

    id = models.AutoField(db_column='serviceid', primary_key=True)
    netbox = models.ForeignKey(Netbox,
                               on_delete=models.CASCADE,
                               db_column='netboxid')
    active = models.BooleanField(default=True)
    handler = VarcharField(verbose_name='service')
    version = VarcharField()
    up = models.CharField(max_length=1, choices=UP_CHOICES, default=UP_UP)

    class Meta(object):
        db_table = 'service'
        ordering = ('handler', )

    def __str__(self):
        return u"{handler} at {netbox}".format(handler=self.handler,
                                               netbox=self.netbox)

    def get_statistics(self):
        args = (self.netbox.sysname, self.handler, self.id)
        avail_id = metric_path_for_service_availability(*args)
        rtime_id = metric_path_for_service_response_time(*args)

        result = {
            'availability': {
                'data_source': avail_id,
            },
            'response_time': {
                'data_source': rtime_id,
            },
        }

        for time_frame in self.TIME_FRAMES:
            avg = get_metric_average([avail_id, rtime_id],
                                     start="-1%s" % time_frame)

            # Availability
            pktloss = avg.get(avail_id, None)
            if pktloss is not None:
                pktloss = 100 - (pktloss * 100)
            result['availability'][time_frame] = pktloss

            # Response time
            result['response_time'][time_frame] = avg.get(rtime_id, None)

        return result

    def is_on_maintenance(self):
        """
        Returns True if this service, or its owning Netbox, is currently on
        maintenance.
        """
        states = self.netbox.get_unresolved_alerts('maintenanceState').filter(
            variables__variable='service', subid=self.id)
        if states.count() < 1:
            return self.netbox.is_on_maintenance()
        else:
            return True

    def last_downtime_ended(self):
        """
        Returns the end_time of the last known serviceState alert.

        :returns: A datetime object if a serviceState alert was found,
                  otherwise None
        """
        try:
            lastdown = self.netbox.alerthistory_set.filter(
                event_type__id='serviceState',
                end_time__isnull=False).order_by("-end_time")[0]
        except IndexError:
            return
        else:
            return lastdown.end_time

    def get_handler_description(self):
        """Returns the description of the handler

        The description is defined in the service checker
        """
        classname = u"{}Checker".format(str(self.handler).capitalize())
        modulename = u"nav.statemon.checker.{}".format(classname)
        checker = __import__(modulename, globals(), locals(), [classname], 0)
        klass = getattr(checker, classname)
        return getattr(klass, 'DESCRIPTION', '')

    description = property(get_handler_description)
Ejemplo n.º 5
0
class MaintenanceTask(models.Model):
    """From NAV Wiki: The maintenance task created in the maintenance task
    tool."""
    objects = MaintenanceTaskManager()

    STATE_SCHEDULED = 'scheduled'
    STATE_ACTIVE = 'active'
    STATE_PASSED = 'passed'
    STATE_CANCELED = 'canceled'
    STATES = (
        (STATE_SCHEDULED, 'Scheduled'),
        (STATE_ACTIVE, 'Active'),
        (STATE_PASSED, 'Passed'),
        (STATE_CANCELED, 'Canceled'),
    )

    id = models.AutoField(db_column='maint_taskid', primary_key=True)
    start_time = models.DateTimeField(db_column='maint_start')
    end_time = DateTimeInfinityField(db_column='maint_end', blank=True)
    description = models.TextField()
    author = VarcharField()
    state = VarcharField(choices=STATES)

    class Meta(object):
        db_table = 'maint_task'

    def __str__(self):
        return u'"%s" by %s' % (self.description, self.author)

    def full_representation(self):
        """
        Help function to represent a task with desc, start and end.
        """
        return u'%s (%s - %s)' % (
            self.description,
            self.start_time,
            ('No end time' if self.is_endless() else self.end_time))

    def get_components(self):
        """
        Returns the list of model objects involved in this task
        """
        return [c.component for c in self.maintenancecomponent_set.all()]

    def get_event_subjects(self):
        """
        Returns a list of the model objects, represented by this task,
        that can be the subjects of actual maintenanceState events.
        """
        subjects = []
        for component in self.get_components():
            if isinstance(component, (manage.Room, manage.NetboxGroup)):
                subjects.extend(component.netbox_set.all())
            elif isinstance(component, manage.Location):
                for location in component.get_descendants(include_self=True):
                    subjects.extend(manage.Netbox.objects.filter(
                        room__location=location))
            elif component is None:
                continue  # no use in including deleted components
            else:
                subjects.append(component)

        return list(set(subjects))

    def is_endless(self):
        """Returns true if the task is endless"""
        return self.end_time >= INFINITY
Ejemplo n.º 6
0
class AlertHistory(models.Model, EventMixIn):
    """From NAV Wiki: The alert history. Simular to the alert queue with one
    important distinction; alert history stores stateful events as one row,
    with the start and end time of the event."""
    objects = AlertHistoryManager()

    id = models.AutoField(db_column='alerthistid', primary_key=True)
    source = models.ForeignKey('Subsystem', db_column='source')
    device = models.ForeignKey('models.Device',
                               db_column='deviceid',
                               null=True)
    netbox = models.ForeignKey('models.Netbox',
                               db_column='netboxid',
                               null=True)
    subid = VarcharField(default='')
    start_time = models.DateTimeField()
    end_time = DateTimeInfinityField(null=True)
    event_type = models.ForeignKey('EventType', db_column='eventtypeid')
    alert_type = models.ForeignKey('AlertType',
                                   db_column='alerttypeid',
                                   null=True)
    value = models.IntegerField()
    severity = models.IntegerField()

    varmap = StateVariableMap()

    class Meta(object):
        db_table = 'alerthist'

    def __unicode__(self):
        return u'Source %s, severity %d' % (self.source, self.severity)

    def is_stateful(self):
        """Returns true if the alert is stateful."""

        return self.end_time is not None

    def is_open(self):
        """Returns true if stateful and open."""

        return self.is_stateful() and self.end_time == dt.datetime.max

    def get_downtime(self):
        """Returns the difference between start_time and end_time, the current
        downtime if the alert is still open, and None if the alert is
        stateless."""

        if self.is_stateful():
            if self.is_open():
                # Open alert
                return (dt.datetime.now() - self.start_time)
            else:
                # Closed alert
                return (self.end_time - self.start_time)
        else:
            # Stateless alert
            return None

    def is_acknowledged(self):
        """
        Returns an Acknowledgement instance if this alert has been
        acknowledged, otherwise None.
        """
        try:
            return self.acknowledgement
        except Acknowledgement.DoesNotExist:
            return

    def acknowledge(self, account, comment):
        """
        Acknowledges this alert using a given account and comment.

        Any pre-existing acknowledgement will be overwritten.
        """
        try:
            ack = self.acknowledgement
        except Acknowledgement.DoesNotExist:
            ack = Acknowledgement(alert=self, account=account, comment=comment)
        else:
            ack.account = account
            ack.comment = comment
            ack.date = dt.datetime.now()

        ack.save()

    def save(self, *args, **kwargs):
        new_object = self.pk is None
        super(AlertHistory, self).save(*args, **kwargs)
        if new_object:
            assert self.pk
            self.varmap = self.varmap
Ejemplo n.º 7
0
Archivo: rack.py Proyecto: wujcheng/nav
class Rack(models.Model):
    """A physical rack placed in a room."""

    objects = RackManager()

    id = models.AutoField(primary_key=True, db_column='rackid')
    room = models.ForeignKey(Room, db_column='roomid')
    rackname = VarcharField(blank=True)
    ordering = models.IntegerField()
    _configuration = VarcharField(default=None, db_column='configuration')
    __configuration = None
    item_counter = models.IntegerField(default=0,
                                       null=False,
                                       db_column='item_counter')

    class Meta(object):
        db_table = 'rack'

    def __str__(self):
        return "'{}' in {}".format(self.rackname or self.id, self.room.pk)

    @property
    def configuration(self):
        """Gets (and sets) the rackitem configuration for this rack

        The rack item configuration is stored as JSONB, and is returned as a
        dict by psycopg.
        """
        if self.__configuration is None:
            if self._configuration is None:
                self._configuration = {}
            self._configuration.setdefault('left', [])
            self._configuration.setdefault('center', [])
            self._configuration.setdefault('right', [])
            self._configuration['left'] = [
                rack_decoder(x) for x in self._configuration['left']
            ]
            self._configuration['right'] = [
                rack_decoder(x) for x in self._configuration['right']
            ]
            self._configuration['center'] = [
                rack_decoder(x) for x in self._configuration['center']
            ]
            self.__configuration = self._configuration

        return self.__configuration

    def save(self, *args, **kwargs):
        self._configuration = json.dumps(self.configuration, cls=RackEncoder)
        return super(Rack, self).save(*args, **kwargs)

    def _column(self, column):
        return self.configuration[column]

    @property
    def left_column(self):
        """Gets all rackitems in the left column"""
        return self._column('left')

    @property
    def right_column(self):
        """Gets all rackitems in the right column"""
        return self._column('right')

    @property
    def center_column(self):
        """Gets all rackitems in the center column"""
        return self._column('center')

    def add_left_item(self, item):
        """
        :type item: RackItem
        """
        self.item_counter += 1
        item.id = self.item_counter
        self.left_column.append(item)

    def add_center_item(self, item):
        """
        :type item: RackItem
        """
        self.item_counter += 1
        item.id = self.item_counter
        self.center_column.append(item)

    def add_right_item(self, item):
        """
        :type item: RackItem
        """
        self.item_counter += 1
        item.id = self.item_counter
        self.right_column.append(item)

    def remove_left_item(self, index):
        """
        :type index: int
        """
        self.left_column.pop(index)

    def remove_center_item(self, index):
        """
        :type index: int
        """
        self.center_column.pop(index)

    def remove_right_item(self, index):
        """
        :type index: int
        """
        self.right_column.pop(index)

    def get_all_sensor_pks(self):
        """Returns an exhaustive list of the primary keys of sensors in this
        rack
        """
        return []
Ejemplo n.º 8
0
class MatchField(models.Model):
    """Defines which fields can be matched upon and how"""

    STRING = 0
    INTEGER = 1
    IP = 2

    # Due to the way alertengine has been reimpleneted the code only really
    # does stuff diffrently if datatype is set to IP, however setting datatype
    # still makes alot of sense in alertprofiles so that we can verify
    # userinput
    DATA_TYPES = (
        (STRING, _('string')),
        (INTEGER, _('integer')),
        (IP, _('ip')),
    )

    # This is a manualy mainted mapping between our model concepts and the
    # actual db tables that are in use. This is needed as our value_id is base
    # on this value.
    ALERT = 'alertq'
    ALERTTYPE = 'alerttype'
    ARP = 'arp'
    CAM = 'cam'
    CATEGORY = 'cat'
    NETBOXGROUP = 'netboxgroup'
    DEVICE = 'device'
    EVENT_TYPE = 'eventtype'
    LOCATION = 'location'
    MEMORY = 'mem'
    MODULE = 'module'
    NETBOX = 'netbox'
    NETBOXINFO = 'netboxinfo'
    ORGANIZATION = 'org'
    PREFIX = 'prefix'
    ROOM = 'room'
    SERVICE = 'service'
    INTERFACE = 'interface'
    TYPE = 'type'
    VENDOR = 'vendor'
    VLAN = 'vlan'
    USAGE = 'usage'

    LOOKUP_FIELDS = (
        (ALERT, _('alert')),
        (ALERTTYPE, _('alert type')),
        (ARP, _('arp')),
        (CAM, _('cam')),
        (CATEGORY, _('category')),
        (NETBOXGROUP, _('netboxgroup')),
        (DEVICE, _('device')),
        (EVENT_TYPE, _('event type')),
        (LOCATION, _('location')),
        (MEMORY, _('memeroy')),
        (MODULE, _('module')),
        (NETBOX, _('netbox')),
        (NETBOXINFO, _('netbox info')),
        (ORGANIZATION, _('organization')),
        (PREFIX, _('prefix')),
        (ROOM, _('room')),
        (SERVICE, _('service')),
        (INTERFACE, _('Interface')),
        (TYPE, _('type')),
        (VENDOR, _('vendor')),
        (VLAN, _('vlan')),
        (USAGE, _('usage')),
    )

    # This mapping designates how a MatchField relates to an alert. (yes the
    # formating is not PEP8, but it wouldn't be very readable otherwise)
    # Since we need to know how things are connected this has been done manualy
    FOREIGN_MAP = {
        ARP: 'netbox__arp',
        CAM: 'netbox__cam',
        CATEGORY: 'netbox__category',
        NETBOXGROUP: 'netbox__netboxcategory__category',
        DEVICE: 'netbox__device',
        EVENT_TYPE: 'event_type',
        LOCATION: 'netbox__room__location',
        MEMORY: 'netbox__memory',
        MODULE: 'netbox__module',
        NETBOX: 'netbox',
        NETBOXINFO: 'netbox__info',
        ORGANIZATION: 'netbox__organization',
        PREFIX: 'netbox__prefix',
        ROOM: 'netbox__room',
        SERVICE: 'netbox__service',
        INTERFACE: 'netbox__connected_to_interface',
        TYPE: 'netbox__type',
        USAGE: 'netbox__organization__vlan__usage',
        VENDOR: 'netbox__type__vendor',
        VLAN: 'netbox__organization__vlan',
        ALERT: '',  # Checks alert object itself
        ALERTTYPE: 'alert_type',
    }

    # Build the mapping we need to be able to do checks.
    VALUE_MAP = {}
    CHOICES = []
    MODEL_MAP = {}

    # This code loops over all the SUPPORTED_MODELS and gets the db_table and
    # db_column so that we can translate them into the correspinding attributes
    # on our django models. (field and model need to be set to None to avoid an
    # ugly side effect of field becoming an acctuall field on MatchField)
    for model in SUPPORTED_MODELS:
        for field in model._meta.fields:
            key = '%s.%s' % (model._meta.db_table, field.db_column
                             or field.attname)
            value = '%s__%s' % (FOREIGN_MAP[model._meta.db_table],
                                field.attname)

            VALUE_MAP[key] = field.attname
            CHOICES.append((key, value.lstrip('_')))
            MODEL_MAP[key] = (model, field.attname)
        field = None
    model = None

    name = VarcharField()
    description = VarcharField(blank=True)
    value_help = VarcharField(
        blank=True,
        help_text=_(u'Help text for the match field. Displayed by the value '
                    u'input box in the GUI to help users enter sane values.'))
    value_id = VarcharField(
        choices=CHOICES,
        help_text=_(u'The "match field". This is the actual database field '
                    u'alert engine will watch.'))
    value_name = VarcharField(
        choices=CHOICES,
        blank=True,
        help_text=_(u'When "show list" is checked, the list will be populated '
                    u'with data from this column as well as the "value id" '
                    u'field. Does nothing else than provide a little more '
                    u'info for the users in the GUI.'))
    value_sort = VarcharField(
        choices=CHOICES,
        blank=True,
        help_text=_(u'Options in the list will be ordered by this field (if '
                    u'not set, options will be ordered by primary key). Only '
                    u'does something when "Show list" is checked.'))
    list_limit = models.IntegerField(
        blank=True,
        help_text=_(u'Only this many options will be available in the list. '
                    u'Only does something when "Show list" is checked.'))
    data_type = models.IntegerField(
        choices=DATA_TYPES, help_text=_(u'The data type of the match field.'))
    show_list = models.BooleanField(
        blank=True,
        help_text=_(u'If unchecked values can be entered into a text input. '
                    u'If checked values must be selected from a list '
                    u'populated by data from the match field selected above.'))

    class Meta(object):
        db_table = u'matchfield'

    def __unicode__(self):
        return self.name

    def get_lookup_mapping(self):
        """Returns the field lookup represented by this MatchField."""
        logger = logging.getLogger(
            'nav.alertengine.matchfield.get_lookup_mapping')

        try:
            foreign_lookup = self.FOREIGN_MAP[self.value_id.split('.')[0]]
            value = self.VALUE_MAP[self.value_id]

            if foreign_lookup:
                return '%s__%s' % (foreign_lookup, value)
            return value

        except KeyError:
            logger.error(
                "Tried to lookup mapping for %s which is not supported",
                self.value_id)
        return None
Ejemplo n.º 9
0
class Account(models.Model):
    """ NAV's basic account model"""

    DEFAULT_ACCOUNT = 0
    ADMIN_ACCOUNT = 1

    # An overview of current preferences.
    # They should start with PREFERENCE_KEY
    PREFERENCE_KEY_LANGUAGE = 'language'  # AlertProfiles
    PREFERENCE_KEY_STATUS = 'status-preferences'
    PREFERENCE_KEY_WIDGET_COLUMNS = 'widget_columns'
    PREFERENCE_KEY_REPORT_PAGE_SIZE = 'report_page_size'
    PREFERENCE_KEY_WIDGET_DISPLAY_DENSITY = 'widget_display_density'

    # FIXME get this from setting.
    MIN_PASSWD_LENGTH = 8

    login = VarcharField(unique=True)
    name = VarcharField()
    password = VarcharField()
    ext_sync = VarcharField()
    preferences = hstore.DictionaryField()

    objects = hstore.HStoreManager()

    organizations = models.ManyToManyField(Organization, db_table='accountorg')

    class Meta(object):
        db_table = u'account'
        ordering = ('login', )

    def __unicode__(self):
        return self.login

    def get_active_profile(self):
        """Returns the account's active alert profile"""
        try:
            return self.alertpreference.active_profile
        except (AlertPreference.DoesNotExist, AlertProfile.DoesNotExist):
            pass

    def get_groups(self):
        """Fetches and returns this users groups.
        Also stores groups in this object for later use.
        """
        try:
            return self._cached_groups
        except AttributeError:
            self._cached_groups = self.accountgroup_set.values_list('id',
                                                                    flat=True)
            return self._cached_groups

    def get_privileges(self):
        """Fetches privileges for this users groups.
        Also stores privileges in this object for later use.
        """
        try:
            return self._cached_privileges
        except AttributeError:
            self._cached_privileges = Privilege.objects.filter(
                group__in=self.get_groups())
            return self._cached_privileges

    def get_tools(self):
        """Get the tool list for this account"""
        return [
            tool for tool in self.accounttool_set.all().order_by('priority')
            if self.has_perm('web_access', tool.tool.uri)
        ]

    def has_perm(self, action, target):
        """Checks if user has permission to do action on target."""
        groups = self.get_groups()
        privileges = self.get_privileges()

        if AccountGroup.ADMIN_GROUP in groups:
            return True
        elif privileges.count() == 0:
            return False
        elif action == 'web_access':
            for privilege in privileges:
                regexp = re.compile(privilege.target)
                if regexp.search(target):
                    return True
            return False
        else:
            return privileges.filter(target=target).count() > 0

    def is_system_account(self):
        """Is this system (undeleteable) account?"""
        return self.id < 1000

    def is_default_account(self):
        """Is this the anonymous user account?"""
        return self.id == self.DEFAULT_ACCOUNT

    def is_admin_account(self):
        """Is this the admin account?"""
        return self.id == self.ADMIN_ACCOUNT

    def is_admin(self):
        """Has this user administrator rights?"""
        return self.has_perm(None, None)

    @sensitive_variables('password')
    def set_password(self, password):
        """Sets user password. Copied from nav.db.navprofiles"""
        if len(password.strip()):
            pw_hash = nav.pwhash.Hash(password=password)
            self.password = str(pw_hash)
        else:
            self.password = ''

    @sensitive_variables('password')
    def check_password(self, password):
        """
        Return True if the submitted authentication tokens are valid
        for this Account.  In simpler terms; when password
        authentication is used, this method compares the given
        password with the one stored for this account and returns true
        if they are equal.  If the stored password is blank, we
        interpret this as: 'The user is not allowed to log in'

        In the future, this could be extended to accept other types of
        authentication tokens, such as personal certificates or
        whatever.

        Copied from nav.db.navprofiles
        """
        # FIXME If password is old style NAV MD5, shouldn't we update the
        # password in the database to be new style password?
        if not self.locked:
            stored_hash = nav.pwhash.Hash()
            try:
                stored_hash.set_hash(self.password)
            except nav.pwhash.InvalidHashStringError:
                # Probably an old style NAV password hash, get out
                # of here and check it the old way
                pass
            else:
                return stored_hash.verify(password)

            # If the stored password looks like an old-style NAV MD5
            # hash we compute the MD5 hash of the supplied password
            # for comparison.
            if self.password[:3] == 'md5':
                pw_hash = md5(password)
                return pw_hash.hexdigest() == self.password[3:]
            else:
                return password == self.password
        else:
            return False

    @property
    def locked(self):
        return not self.password or self.password.startswith('!')

    @locked.setter
    def locked(self, value):
        if not value and self.password.startswith('!'):
            self.password = self.password[1:]
        elif value and not self.password.startswith('!'):
            self.password = '******' + self.password
Ejemplo n.º 10
0
class Filter(models.Model):
    """One or more expressions that are combined with an and operation.

    Handles the actual construction of queries to be run taking into account
    special cases like the IP datatype and WILDCARD lookups."""

    owner = models.ForeignKey('Account', null=True)
    name = VarcharField()

    class Meta(object):
        db_table = u'filter'

    def __unicode__(self):
        return self.name

    def check(self, alert):
        """Combines expressions to an ORM query that will tell us if an alert
        matched.

        This function builds three dicts that are used in the ORM .filter()
        .exclude() and .extra() methods which finally gets a .count() as we
        only need to know if something matched.

        Running alertengine in debug mode will print the dicts to the logs.

        """
        logger = logging.getLogger('nav.alertengine.filter.check')

        filtr = {}
        exclude = {}
        extra = {'where': [], 'params': []}

        for expression in self.expression_set.all():
            # Handle IP datatypes:
            if expression.match_field.data_type == MatchField.IP:
                # Trick the ORM into joining the tables we want
                lookup = ('%s__isnull' %
                          expression.match_field.get_lookup_mapping())
                filtr[lookup] = False

                where = Operator(
                    type=expression.operator).get_ip_operator_mapping()

                if expression.operator in [Operator.IN, Operator.CONTAINS]:
                    values = expression.value.split('|')
                    where = ' OR '.join(
                        [where % expression.match_field.value_id] *
                        len(values))

                    extra['where'].append('(%s)' % where)
                    extra['params'].extend(values)

                else:
                    # Get the IP mapping and put in the field before adding it
                    # to our where clause.
                    extra['where'].append(where %
                                          expression.match_field.value_id)
                    extra['params'].append(expression.value)

            # Handle wildcard lookups which are not directly supported by
            # django (as far as i know)
            elif expression.operator == Operator.WILDCARD:
                # Trick the ORM into joining the tables we want
                lookup = ('%s__isnull' %
                          expression.match_field.get_lookup_mapping())
                filtr[lookup] = False

                extra['where'].append('%s ILIKE %%s' %
                                      expression.match_field.value_id)
                extra['params'].append(expression.value)

            # Handle the plain lookups that we can do directly in ORM
            else:
                lookup = (expression.match_field.get_lookup_mapping() +
                          expression.get_operator_mapping())

                # Ensure that in and not equal are handeled correctly
                if expression.operator == Operator.IN:
                    filtr[lookup] = expression.value.split('|')
                elif expression.operator == Operator.NOT_EQUAL:
                    exclude[lookup] = expression.value
                else:
                    filtr[lookup] = expression.value

        # Limit ourselves to our alert
        filtr['id'] = alert.id

        if not extra['where']:
            extra = {}

        logger.debug(
            'alert %d: checking against filter %d with filter: %s, exclude: '
            '%s and extra: %s', alert.id, self.id, filtr, exclude, extra)

        # Check the alert maches whith a SELECT COUNT(*) FROM .... so that the
        # db doesn't have to work as much.
        if AlertQueue.objects.filter(**filtr).exclude(**exclude).extra(
                **extra).count():
            logger.debug('alert %d: matches filter %d', alert.id, self.id)
            return True

        logger.debug('alert %d: did not match filter %d', alert.id, self.id)
        return False
Ejemplo n.º 11
0
class AlertProfile(models.Model):
    """Account AlertProfiles"""

    # Weekday numbers follows date.weekday(), not day.isoweekday().
    MONDAY = 0
    TUESDAY = 1
    WEDNESDAY = 2
    THURSDAY = 3
    FRIDAY = 4
    SATURDAY = 5
    SUNDAY = 6

    VALID_WEEKDAYS = (
        (MONDAY, _('monday')),
        (TUESDAY, _('tuesday')),
        (WEDNESDAY, _('wednesday')),
        (THURSDAY, _('thursday')),
        (FRIDAY, _('friday')),
        (SATURDAY, _('saturday')),
        (SUNDAY, _('sunday')),
    )

    account = models.ForeignKey('Account', db_column='accountid')
    name = VarcharField()
    daily_dispatch_time = models.TimeField(default='08:00')
    weekly_dispatch_day = models.IntegerField(choices=VALID_WEEKDAYS,
                                              default=MONDAY)
    weekly_dispatch_time = models.TimeField(default='08:00')

    class Meta(object):
        db_table = u'alertprofile'

    def __unicode__(self):
        return self.name

    def get_active_timeperiod(self):
        """Gets the currently active timeperiod for this profile"""
        # Could have been done with a ModelManager, but the logic
        # is somewhat tricky to do with the django ORM.

        logger = logging.getLogger(
            'nav.alertengine.alertprofile.get_active_timeperiod')

        now = datetime.now()

        # Limit our query to the correct type of time periods
        if now.isoweekday() in [6, 7]:
            valid_during = [TimePeriod.ALL_WEEK, TimePeriod.WEEKENDS]
        else:
            valid_during = [TimePeriod.ALL_WEEK, TimePeriod.WEEKDAYS]

        # The following code should get the currently active timeperiod.
        active_timeperiod = None
        timeperiods = list(
            self.timeperiod_set.filter(
                valid_during__in=valid_during).order_by('start'))
        # If the current time is before the start of the first time
        # period, the active time period is the last one (i.e. from
        # the day before)
        if len(timeperiods) > 0 and timeperiods[0].start > now.time():
            active_timeperiod = timeperiods[-1]
        else:
            for period in timeperiods:
                if period.start <= now.time():
                    active_timeperiod = period

        if active_timeperiod:
            logger.debug("Active timeperiod for alertprofile %d is %s (%d)",
                         self.id, active_timeperiod, active_timeperiod.id)
        else:
            logger.debug("No active timeperiod for alertprofile %d", self.id)

        return active_timeperiod
Ejemplo n.º 12
0
class AlertAddress(models.Model):
    """Accounts alert addresses, valid types are retrived from
    alertengine.conf

    """
    DEBUG_MODE = False

    account = models.ForeignKey('Account', db_column='accountid')
    type = models.ForeignKey('AlertSender', db_column='type')
    address = VarcharField()

    class Meta(object):
        db_table = u'alertaddress'

    def __unicode__(self):
        return self.type.scheme() + self.address

    @transaction.atomic
    def send(self, alert, subscription):
        """Handles sending of alerts to with defined alert notification types

           Return value should indicate if message was sent"""

        logger = logging.getLogger('nav.alertengine.alertaddress.send')

        # Determine the right language for the user.
        lang = self.account.preferences.get(Account.PREFERENCE_KEY_LANGUAGE,
                                            'en')

        if not (self.address or '').strip():
            logger.error(
                'Ignoring alert %d (%s: %s)! Account %s does not have an '
                'address set for the alertaddress with id %d, this needs '
                'to be fixed before the user will recieve any alerts.',
                alert.id, alert, alert.netbox, self.account, self.id)

            return True

        if self.type.is_blacklisted():
            logger.warning(
                'Not sending alert %s to %s as handler %s is blacklisted: %s',
                alert.id, self.address, self.type,
                self.type.blacklist_reason())
            return False

        try:
            self.type.send(self, alert, language=lang)
            logger.info('alert %d sent by %s to %s due to %s subscription %d',
                        alert.id, self.type, self.address,
                        subscription.get_type_display(), subscription.id)

        except FatalDispatcherException as error:
            logger.error(
                '%s raised a FatalDispatcherException indicating that the '
                'alert never will be sent: %s', self.type, error)
            raise

        except DispatcherException as error:
            logger.error(
                '%s raised a DispatcherException indicating that an alert '
                'could not be sent at this time: %s', self.type, error)
            return False

        except Exception as error:
            logger.exception(
                'Unhandled error from %s (the handler has been blacklisted)',
                self.type)
            self.type.blacklist(error)
            return False

        return True
Ejemplo n.º 13
0
class AlertQueue(models.Model, EventMixIn):
    """From NAV Wiki: The alert queue. Additional data in alertqvar and
    alertmsg. Event engine posts alerts on the alert queue (and in addition on
    the alerthist table). Alert engine will process the data on the alert queue
    and send alerts to users based on their alert profiles. When all signed up
    users have received the alert, alert engine will delete the alert from
    alertq (but not from alert history)."""

    STATE_STATELESS = STATE_STATELESS
    STATE_START = STATE_START
    STATE_END = STATE_END
    STATE_CHOICES = STATE_CHOICES

    id = models.AutoField(db_column='alertqid', primary_key=True)
    source = models.ForeignKey('Subsystem',
                               on_delete=models.CASCADE,
                               db_column='source')
    device = models.ForeignKey('models.Device',
                               on_delete=models.CASCADE,
                               db_column='deviceid',
                               null=True)
    netbox = models.ForeignKey('models.Netbox',
                               on_delete=models.CASCADE,
                               db_column='netboxid',
                               null=True)
    subid = VarcharField(default='')
    time = models.DateTimeField()
    event_type = models.ForeignKey('EventType',
                                   on_delete=models.CASCADE,
                                   db_column='eventtypeid')
    alert_type = models.ForeignKey('AlertType',
                                   on_delete=models.CASCADE,
                                   db_column='alerttypeid',
                                   null=True)
    state = models.CharField(max_length=1,
                             choices=STATE_CHOICES,
                             default=STATE_STATELESS)
    value = models.IntegerField()
    severity = models.IntegerField()

    history = models.ForeignKey('AlertHistory',
                                on_delete=models.CASCADE,
                                null=True,
                                blank=True,
                                db_column='alerthistid')

    varmap = VariableMap()

    class Meta(object):
        db_table = 'alertq'

    def __str__(self):
        return u'Source %s, state %s, severity %d' % (
            self.source, self.get_state_display(), self.severity)

    def save(self, *args, **kwargs):
        new_object = self.pk is None
        super(AlertQueue, self).save(*args, **kwargs)
        if new_object:
            assert self.pk
            self.varmap = self.varmap
Ejemplo n.º 14
0
class EventQueue(models.Model, EventMixIn):
    """From NAV Wiki: The event queue. Additional data in eventqvar. Different
    subsystem (specified in source) post events on the event queue. Normally
    event engine is the target and will take the event off the event queue and
    process it.  getDeviceData are in some cases the target."""

    STATE_STATELESS = STATE_STATELESS
    STATE_START = STATE_START
    STATE_END = STATE_END
    STATE_CHOICES = STATE_CHOICES

    id = models.AutoField(db_column='eventqid', primary_key=True)
    source = models.ForeignKey('Subsystem',
                               on_delete=models.CASCADE,
                               db_column='source',
                               related_name='source_of_events')
    target = models.ForeignKey('Subsystem',
                               on_delete=models.CASCADE,
                               db_column='target',
                               related_name='target_of_events')
    device = models.ForeignKey('models.Device',
                               on_delete=models.CASCADE,
                               db_column='deviceid',
                               null=True)
    netbox = models.ForeignKey('models.Netbox',
                               on_delete=models.CASCADE,
                               db_column='netboxid',
                               null=True)
    subid = VarcharField(default='')
    time = models.DateTimeField(default=dt.datetime.now)
    event_type = models.ForeignKey('EventType',
                                   on_delete=models.CASCADE,
                                   db_column='eventtypeid')
    state = models.CharField(max_length=1,
                             choices=STATE_CHOICES,
                             default=STATE_STATELESS)
    value = models.IntegerField(default=100)
    severity = models.IntegerField(default=50)

    varmap = VariableMap()

    class Meta(object):
        db_table = 'eventq'

    def __repr__(self):
        return "<EventQueue: %s>" % u", ".join(
            u"%s=%r" % (attr, getattr(self, attr))
            for attr in ('id', 'event_type_id', 'source_id', 'target_id',
                         'netbox', 'subid', 'state', 'time'))

    def __str__(self):
        string = ("{self.event_type} {state} event for {self.netbox} "
                  "(subid={self.subid}) from {self.source} to {self.target} "
                  "at {self.time}")
        return string.format(self=self,
                             state=dict(self.STATE_CHOICES)[self.state])

    def save(self, *args, **kwargs):
        new_object = self.pk is None
        super(EventQueue, self).save(*args, **kwargs)
        if new_object:
            assert self.pk
            self.varmap = self.varmap
Ejemplo n.º 15
0
class Image(models.Model):
    """Model representing an uploaded image"""

    id = models.AutoField(db_column='imageid', primary_key=True)
    room = models.ForeignKey(
        Room, on_delete=models.CASCADE, db_column='roomid', null=True
    )
    location = models.ForeignKey(
        Location, on_delete=models.CASCADE, db_column='locationid', null=True
    )
    title = VarcharField()
    path = VarcharField()
    name = VarcharField()
    created = models.DateTimeField(auto_now_add=True)
    uploader = models.ForeignKey(
        Account, on_delete=models.CASCADE, db_column='uploader'
    )
    priority = models.IntegerField()

    class Meta(object):
        db_table = 'image'
        ordering = ['priority']

    def _check_image_existance(self):
        return exists(self.fullpath)

    def _check_thumb_existance(self):
        """Relies on static thumb directory"""
        return exists(self.thumbpath)

    def _check_readable(self):
        return os.access(self.fullpath, os.R_OK)

    def _get_url(self):
        return '/uploads/images/{itype}/{path}/{name}'.format(
            itype=self._type(), path=self.path, name=self.name
        )

    def _get_thumb_url(self):
        return '/uploads/images/{itype}/{path}/thumbs/{name}'.format(
            itype=self._type(), path=self.path, name=self.name
        )

    def _type(self):
        if self.room:
            return 'rooms'
        return 'locations'

    def _get_basepath(self):
        return join(settings.UPLOAD_DIR, 'images', self._type())

    def _get_fullpath(self):
        return join(self.basepath, self.path, self.name)

    def _get_thumb_path(self):
        return join(self.basepath, self.path, 'thumbs', self.name)

    image_exists = property(_check_image_existance)
    thumb_exists = property(_check_thumb_existance)
    is_readable = property(_check_readable)
    url = property(_get_url)
    thumb_url = property(_get_thumb_url)
    basepath = property(_get_basepath)
    fullpath = property(_get_fullpath)
    thumbpath = property(_get_thumb_path)
Ejemplo n.º 16
0
class Identity(models.Model):
    """
    The table contains a listing for each computer,interface combo Arnold
    has blocked.
    """

    id = models.AutoField(db_column='identityid', primary_key=True)
    mac = models.CharField(db_column='mac', max_length=17)
    status = VarcharField(db_column='blocked_status', choices=STATUSES)
    justification = models.ForeignKey('Justification',
                                      on_delete=models.CASCADE,
                                      db_column='blocked_reasonid')
    interface = models.ForeignKey(Interface,
                                  on_delete=models.CASCADE,
                                  db_column='swportid')
    ip = models.GenericIPAddressField(null=True, default='0.0.0.0')
    dns = VarcharField(blank=True)
    netbios = VarcharField(blank=True)
    first_offence = models.DateTimeField(db_column='starttime',
                                         auto_now_add=True)
    last_changed = models.DateTimeField(db_column='lastchanged', auto_now=True)
    autoenable = models.DateTimeField(null=True)
    autoenablestep = models.IntegerField(null=True, default=2)
    mail = VarcharField(blank=True)
    organization = models.ForeignKey('Organization',
                                     on_delete=models.CASCADE,
                                     db_column='orgid',
                                     null=True)
    keep_closed = models.CharField(db_column='determined',
                                   default='n',
                                   choices=KEEP_CLOSED_CHOICES,
                                   max_length=1)
    fromvlan = models.IntegerField(null=True)
    tovlan = models.ForeignKey(
        'QuarantineVlan',
        on_delete=models.CASCADE,
        db_column='tovlan',
        to_field='vlan',
        null=True,
        default=None,
    )
    # If the interface does not exist any longer in the database, the user
    # needs a hint of what interface was blocked as information as ifname
    # and netbox naturally no longer exists based on interfaceid.
    # This fields solves this by storing the textual representation of the
    # interface, that can be displayed if the situation occurs.
    # The format is "interface.ifname at interface.netbox.sysname"
    textual_interface = VarcharField(default='')

    def __str__(self):
        try:
            interface = self.interface
        except Interface.DoesNotExist:
            interface = "N/A"
        return "%s/%s %s" % (self.ip, self.mac, interface)

    class Meta(object):
        db_table = 'identity'
        ordering = ('last_changed', )
        verbose_name = 'identity'
        verbose_name_plural = 'identities'
        unique_together = ('mac', 'interface')