distcloud/dcorch/snmp/controller.py

106 lines
4.2 KiB
Python

# Copyright (c) 2017 Ericsson AB.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import collections
import datetime
from dcorch.common import consts
from dcorch.common import context
from dcorch.rpc import client as rpc_client
from multiprocessing import Queue
from oslo_log import log as logging
LOG = logging.getLogger(__name__)
class Controller(object):
system_throttle_timers = {}
system_last_updates = {}
system_in_delay = {}
system_trap_tstamps = {}
def __init__(self, systems, cfg):
self.cfg = cfg
self.event_queue = Queue()
self.rpc_client = rpc_client.EngineClient()
self.throttle_threshold = self.cfg.snmp.throttle_threshold
for i in systems:
self._add_system(i)
def send_notification(self, system):
LOG.debug("Sending update request for %s" % (system))
try:
ctx = context.get_admin_context()
self.rpc_client.update_alarm_summary(ctx, system)
except Exception:
LOG.error('Failed to send update for system %s' % system)
return
self.system_last_updates[system] = datetime.datetime.now()
def _add_system(self, system):
# Arbitrarily distant last update, ensures first trap updates
self.system_last_updates[system] = datetime.datetime(1989, 3, 9)
self.system_throttle_timers[system] = None
self.system_in_delay[system] = False
self.system_trap_tstamps[system] = collections.deque()
def handle_trap(self, system, msg):
if system == consts.CLOUD_0:
return
if not (system in self.system_last_updates):
self._add_system(system)
tstamp = datetime.datetime.utcnow()
self.system_trap_tstamps[system].append(tstamp)
# we throttle the notification in the following condiftions
# 1. system is already being throttled (ignores notification)
# 2. If more than throttle_threshold traps have come within
# delay_time and we last updated within delay_time
# otherwise we request an update for the system
if len(self.system_trap_tstamps[system]) > self.throttle_threshold:
self.system_trap_tstamps[system].popleft()
if self.system_in_delay[system]:
LOG.debug("No action for %s , msg: %s. Already in delay" %
(system, msg))
return
if self._should_throttle_notification(system, tstamp):
delta = (tstamp -
self.system_last_updates[system]).total_seconds()
if delta > self.cfg.snmp.delay_time:
self.send_notification(system)
else:
notification_time = self.system_last_updates[system] +\
datetime.timedelta(0, self.cfg.snmp.delay_time)
self.system_throttle_timers[system] = notification_time
else:
self.send_notification(system)
def _should_throttle_notification(self, system, new_trap_tstamp):
d = self.system_trap_tstamps[system]
if len(d) < self.throttle_threshold:
return False
if d[0] < new_trap_tstamp -\
datetime.timedelta(0, self.cfg.snmp.delay_time):
return False
return True
def handle_delayed_notifications(self):
curr_time = datetime.datetime.utcnow()
for system, notify_time in self.system_throttle_timers.items():
if notify_time is not None:
if curr_time > notify_time:
self.send_notification(system)
self.system_throttle_timers[system] = None
self.system_in_delay[system] = False