示例#1
0
class EventBusPublisher(object):
    def __init__(self, kafka_proxy, config):
        self.kafka_proxy = kafka_proxy
        self.config = config
        self.topic_mappings = config.get('topic_mappings', {})
        self.event_bus = EventBusClient()
        self.subscriptions = None

    def start(self):
        log.debug('starting')
        self.subscriptions = list()
        self._setup_subscriptions(self.topic_mappings)
        log.info('started')
        return self

    def stop(self):
        try:
            log.debug('stopping-event-bus')
            if self.subscriptions:
                for subscription in self.subscriptions:
                    self.event_bus.unsubscribe(subscription)
            log.info('stopped-event-bus')
        except Exception, e:
            log.exception('failed-stopping-event-bus', e=e)
            return
示例#2
0
    def test_subscribe(self):

        ebc = EventBusClient()
        sub = ebc.subscribe('news', lambda msg, topic: None)
        self.assertEqual(len(ebc.list_subscribers()), 1)
        self.assertEqual(len(ebc.list_subscribers('news')), 1)
        self.assertEqual(len(ebc.list_subscribers('other')), 0)
示例#3
0
    def __init__(self, adapter_agent, device_id, me_map=None,
                 alarm_queue_limit=_MAX_INCOMING_ALARM_MESSAGES,
                 avc_queue_limit=_MAX_INCOMING_ALARM_MESSAGES,
                 test_results_queue_limit=_MAX_INCOMING_TEST_RESULT_MESSAGES):
        self.log = structlog.get_logger(device_id=device_id)
        self._adapter_agent = adapter_agent
        self._device_id = device_id
        self._proxy_address = None
        self._tx_tid = 1
        self._enabled = False
        self._requests = dict()       # Tx ID -> (timestamp, deferred, tx_frame, timeout)
        self._alarm_queue = DeferredQueue(size=alarm_queue_limit)
        self._avc_queue = DeferredQueue(size=avc_queue_limit)
        self._test_results_queue = DeferredQueue(size=test_results_queue_limit)
        self._me_map = me_map

        # Statistics
        self._tx_frames = 0
        self._rx_frames = 0
        self._rx_unknown_tid = 0      # Rx OMCI with no Tx TID match
        self._rx_onu_frames = 0       # Autonomously generated ONU frames
        self._rx_alarm_overflow = 0   # Autonomously generated ONU alarms rx overflow
        self._rx_avc_overflow = 0     # Autonomously generated ONU AVC rx overflow
        self._rx_onu_discards = 0     # Autonomously generated ONU unknown message types
        self._rx_timeouts = 0
        self._rx_unknown_me = 0       # Number of managed entities Rx without a decode definition
        self._tx_errors = 0           # Exceptions during tx request
        self._consecutive_errors = 0  # Rx & Tx errors in a row, a good RX resets this to 0
        self._reply_min = sys.maxint  # Fastest successful tx -> rx
        self._reply_max = 0           # Longest successful tx -> rx
        self._reply_sum = 0.0         # Total seconds for successful tx->rx (float for average)

        self.event_bus = EventBusClient()
示例#4
0
class OpenOmciEventBus(object):
    """ Event bus for publishing OpenOMCI related events. """
    __slots__ = (
        '_event_bus_client',  # The event bus client used to publish events.
        '_topic'  # the topic to publish to
    )

    def __init__(self):
        self._event_bus_client = EventBusClient()
        self._topic = 'openomci-events'

    def message_to_dict(m):
        return MessageToDict(m, True, True, False)

    def advertise(self, event_type, data):
        if isinstance(data, Message):
            msg = dumps(MessageToDict(data, True, True))
        elif isinstance(data, dict):
            msg = dumps(data)
        else:
            msg = str(data)

        event_func = AlarmOpenOmciEvent if 'AlarmSynchronizer' in msg \
                                  else OpenOmciEvent
        event = event_func(type=event_type, data=msg)

        self._event_bus_client.publish(self._topic, event)
class ConfigEventBus(object):

    __slots__ = (
        '_event_bus_client',  # The event bus client used to publish events.
        '_topic'  # the topic to publish to
    )

    def __init__(self):
        self._event_bus_client = EventBusClient()
        self._topic = 'model-change-events'

    def advertise(self, type, data, hash=None):
        if type in IGNORED_CALLBACKS:
            log.info('Ignoring event {} with data {}'.format(type, data))
            return

        if type is CallbackType.POST_ADD:
            kind = ConfigEventType.add
        elif type is CallbackType.POST_REMOVE:
            kind = ConfigEventType.remove
        else:
            kind = ConfigEventType.update

        if isinstance(data, Message):
            msg = dumps(MessageToDict(data, True, True))
        else:
            msg = data

        event = ConfigEvent(
            type=kind,
            hash=hash,
            data=msg
        )

        self._event_bus_client.publish(self._topic, event)
示例#6
0
 def __init__(self, config):
     self.config = config
     self.periodic_check_interval = config.get('periodic_check_interval',
                                               15)
     self.periodic_checks = None
     self.event_bus = EventBusClient()
     self.instance_id = registry('main').get_args().instance_id
示例#7
0
    def __init__(self, core, logical_device):
        try:
            self.core = core
            self.local_handler = core.get_local_handler()
            self.logical_device_id = logical_device.id

            self.root_proxy = core.get_proxy('/')
            self.flows_proxy = core.get_proxy(
                '/logical_devices/{}/flows'.format(logical_device.id))
            self.groups_proxy = core.get_proxy(
                '/logical_devices/{}/flow_groups'.format(logical_device.id))
            self.self_proxy = core.get_proxy(
                '/logical_devices/{}'.format(logical_device.id))

            self.flows_proxy.register_callback(
                CallbackType.POST_UPDATE, self._flow_table_updated)
            self.groups_proxy.register_callback(
                CallbackType.POST_UPDATE, self._group_table_updated)
            self.self_proxy.register_callback(
                CallbackType.POST_ADD, self._port_added)
            self.self_proxy.register_callback(
                CallbackType.POST_REMOVE, self._port_removed)

            self.port_proxy = {}

            self.event_bus = EventBusClient()
            self.packet_in_subscription = self.event_bus.subscribe(
                topic='packet-in:{}'.format(logical_device.id),
                callback=self.handle_packet_in_event)

            self.log = structlog.get_logger(logical_device_id=logical_device.id)

            self._routes = None
        except Exception, e:
            self.log.exception('init-error', e=e)
class EventBusPublisher(object):

    def __init__(self, kafka_proxy, config):
        self.kafka_proxy = kafka_proxy
        self.config = config
        self.topic_mappings = config.get('topic_mappings', {})
        self.event_bus = EventBusClient()
        self.subscriptions = None

    def start(self):
        log.debug('starting')
        self.subscriptions = list()
        self._setup_subscriptions(self.topic_mappings)
        log.info('started')
        return self

    def stop(self):
        try:
            log.debug('stopping-event-bus')
            if self.subscriptions:
                for subscription in self.subscriptions:
                    self.event_bus.unsubscribe(subscription)
            log.info('stopped-event-bus')
        except Exception, e:
            log.exception('failed-stopping-event-bus', e=e)
            return
示例#9
0
class ConfigEventBus(object):

    __slots__ = (
        '_event_bus_client',  # The event bus client used to publish events.
        '_topic'  # the topic to publish to
    )

    def __init__(self):
        self._event_bus_client = EventBusClient()
        self._topic = 'model-change-events'

    def advertise(self, type, data, hash=None):
        if type in IGNORED_CALLBACKS:
            log.info('Ignoring event {} with data {}'.format(type, data))
            return

        if type is CallbackType.POST_ADD:
            kind = ConfigEventType.add
        elif type is CallbackType.POST_REMOVE:
            kind = ConfigEventType.remove
        else:
            kind = ConfigEventType.update

        if isinstance(data, Message):
            msg = dumps(MessageToDict(data, True, True))
        else:
            msg = data

        event = ConfigEvent(type=kind, hash=hash, data=msg)

        self._event_bus_client.publish(self._topic, event)
示例#10
0
class TestEventLogic(DeepTestsBase):

    def setUp(self):
        super(TestEventLogic, self).setUp()
        self.ebc = EventBusClient()
        self.event_mock = Mock()
        self.ebc.subscribe('model-change-events', self.event_mock)

    def test_add_event(self):

        data = Adapter(id='10', version='zoo')
        self.node.add('/adapters', data)
        event = ConfigEvent(
            type=ConfigEventType.add,
            hash=self.node.latest.hash,
            data=dumps(MessageToDict(data, True, True))
        )

        self.event_mock.assert_called_once_with('model-change-events', event)

    def test_remove_event(self):
        data = Adapter(
            id='1',
            config=AdapterConfig(
                log_level=3
            )
        )
        self.node.remove('/adapters/1')
        event = ConfigEvent(
            type=ConfigEventType.remove,
            hash=self.node.latest.hash,
            data=dumps(MessageToDict(data, True, True))
        )

        self.event_mock.assert_called_once_with('model-change-events', event)
示例#11
0
    def __init__(self, adapter_agent, device_id, me_map=None, clock=None):
        self.log = structlog.get_logger(device_id=device_id)
        self._adapter_agent = adapter_agent
        self._device_id = device_id
        self._proxy_address = None
        self._tx_tid = 1
        self._enabled = False
        self._requests = dict(
        )  # Tx ID -> (timestamp, deferred, tx_frame, timeout, retry, delayedCall)
        self._me_map = me_map
        if clock is None:
            self.reactor = reactor
        else:
            self.reactor = clock

        # Statistics
        self._tx_frames = 0
        self._rx_frames = 0
        self._rx_unknown_tid = 0  # Rx OMCI with no Tx TID match
        self._rx_onu_frames = 0  # Autonomously generated ONU frames
        self._rx_onu_discards = 0  # Autonomously generated ONU unknown message types
        self._rx_timeouts = 0
        self._rx_unknown_me = 0  # Number of managed entities Rx without a decode definition
        self._tx_errors = 0  # Exceptions during tx request
        self._consecutive_errors = 0  # Rx & Tx errors in a row, a good RX resets this to 0
        self._reply_min = sys.maxint  # Fastest successful tx -> rx
        self._reply_max = 0  # Longest successful tx -> rx
        self._reply_sum = 0.0  # Total seconds for successful tx->rx (float for average)

        self.event_bus = EventBusClient()
示例#12
0
    def test_simple_publish(self):

        ebc = EventBusClient(EventBus())

        mock = Mock()
        ebc.subscribe('news', mock)

        ebc.publish('news', 'message')

        self.assertEqual(mock.call_count, 1)
        mock.assert_called_with('news', 'message')
示例#13
0
    def test_topic_filtering(self):

        ebc = EventBusClient(EventBus())

        mock = Mock()
        ebc.subscribe('news', mock)

        ebc.publish('news', 'msg1')
        ebc.publish('alerts', 'msg2')
        ebc.publish('logs', 'msg3')

        self.assertEqual(mock.call_count, 1)
        mock.assert_called_with('news', 'msg1')
示例#14
0
class Diagnostics(object):

    def __init__(self, config):
        self.config = config
        self.periodic_check_interval = config.get(
            'periodic_check_interval', 15)
        self.periodic_checks = None
        self.event_bus = EventBusClient()
        self.instance_id = registry('main').get_args().instance_id

    def start(self):
        log.debug('starting')
        self.periodic_checks = LoopingCall(self.run_periodic_checks)
        self.periodic_checks.start(self.periodic_check_interval)
        log.info('started')
        return self

    def stop(self):
        log.debug('stopping')
        if self.periodic_checks is not None:
            self.periodic_checks.stop()
        log.info('stopped')

    def run_periodic_checks(self):

        ts = arrow.utcnow().timestamp

        def deferreds():
            return len(gc.get_referrers(Deferred))

        def rss_mb():
            rss = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss/1024
            if sys.platform.startswith('darwin'):
                rss /= 1024
            return rss

        kpi_event = KpiEvent(
            type=KpiEventType.slice,
            ts=ts,
            prefixes={
                'voltha.internal.{}'.format(self.instance_id):
                    MetricValuePairs(metrics={
                        'deferreds': deferreds(),
                        'rss-mb': rss_mb(),
                    })
            }
        )

        self.event_bus.publish('kpis', kpi_event)
        log.debug('periodic-check', ts=ts)
示例#15
0
class Diagnostics(object):
    def __init__(self, config):
        self.config = config
        self.periodic_check_interval = config.get('periodic_check_interval',
                                                  15)
        self.periodic_checks = None
        self.event_bus = EventBusClient()
        self.instance_id = registry('main').get_args().instance_id

    def start(self):
        log.debug('starting')
        self.periodic_checks = LoopingCall(self.run_periodic_checks)
        self.periodic_checks.start(self.periodic_check_interval)
        log.info('started')
        return self

    def stop(self):
        log.debug('stopping')
        if self.periodic_checks is not None:
            self.periodic_checks.stop()
        log.info('stopped')

    def run_periodic_checks(self):

        ts = arrow.utcnow().float_timestamp

        def deferreds():
            return len(gc.get_referrers(Deferred))

        def rss_mb():
            rss = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss / 1024
            if sys.platform.startswith('darwin'):
                rss /= 1024
            return rss

        kpi_event = KpiEvent2(
            type=KpiEventType.slice,
            ts=ts,
            slice_data=[
                MetricInformation(metadata=MetricMetaData(
                    title='voltha.internal',
                    ts=ts,
                    context={'instance_id': self.instance_id}),
                                  metrics={
                                      'deferreds': deferreds(),
                                      'rss-mb': rss_mb()
                                  })
            ])
        self.event_bus.publish('kpis', kpi_event)
        log.debug('periodic-check', ts=ts)
    def __init__(self, core, logical_device):
        try:
            self.core = core
            self.local_handler = core.get_local_handler()
            self.logical_device_id = logical_device.id

            self.root_proxy = core.get_proxy('/')
            self.flows_proxy = core.get_proxy(
                '/logical_devices/{}/flows'.format(logical_device.id))
            self.groups_proxy = core.get_proxy(
                '/logical_devices/{}/flow_groups'.format(logical_device.id))
            self.self_proxy = core.get_proxy(
                '/logical_devices/{}'.format(logical_device.id))

            self.flows_proxy.register_callback(
                CallbackType.POST_UPDATE, self._flow_table_updated)
            self.groups_proxy.register_callback(
                CallbackType.POST_UPDATE, self._group_table_updated)
            self.self_proxy.register_callback(
                CallbackType.POST_ADD, self._port_added)
            self.self_proxy.register_callback(
                CallbackType.POST_REMOVE, self._port_removed)

            self.port_proxy = {}

            self.event_bus = EventBusClient()
            self.packet_in_subscription = self.event_bus.subscribe(
                topic='packet-in:{}'.format(logical_device.id),
                callback=self.handle_packet_in_event)

            self.log = structlog.get_logger(logical_device_id=logical_device.id)

            self._routes = None
        except Exception, e:
            self.log.exception('init-error', e=e)
示例#17
0
 def __init__(self, config):
     self.config = config
     self.periodic_check_interval = config.get(
         'periodic_check_interval', 15)
     self.periodic_checks = None
     self.event_bus = EventBusClient()
     self.instance_id = registry('main').get_args().instance_id
示例#18
0
文件: main.py 项目: zychen123/voltha
 def start_manhole(self, port):
     self.manhole = Manhole(
         port,
         pws=dict(admin='adminpw'),
         eventbus = EventBusClient(),
         **registry.components
     )
示例#19
0
    def __init__(self, omci_agent, device_id, adapter_agent, custom_me_map,
                 mib_db, support_classes):
        """
        Class initializer

        :param omci_agent: (OpenOMCIAgent) Reference to OpenOMCI Agent
        :param device_id: (str) ONU Device ID
        :param adapter_agent: (AdapterAgent) Adapter agent for ONU
        :param custom_me_map: (dict) Additional/updated ME to add to class map
        :param mib_db: (MibDbApi) MIB Database reference
        :param support_classes: (dict) State machines and tasks for this ONU
        """
        self.log = structlog.get_logger(device_id=device_id)

        self._started = False
        self._omci_agent = omci_agent  # OMCI AdapterAgent
        self._device_id = device_id  # ONU Device ID
        self._runner = TaskRunner(device_id)  # OMCI_CC Task runner
        self._deferred = None
        self._first_in_sync = False
        self._support_classes = support_classes

        try:
            self._mib_db_in_sync = False
            mib_synchronizer_info = support_classes.get('mib-synchronizer')
            self.mib_sync = mib_synchronizer_info['state-machine'](
                self._omci_agent, device_id, mib_synchronizer_info['tasks'],
                mib_db)
        except Exception as e:
            self.log.exception('mib-sync-create-failed', e=e)
            raise

        self._state_machines = []
        self._on_start_state_machines = [self.mib_sync
                                         ]  # Run when 'start()' called
        self._on_sync_state_machines = []  # Run after first in_sync event

        self._custom_me_map = custom_me_map
        self._me_map = omci_entities.entity_id_to_class_map.copy()

        if custom_me_map is not None:
            self._me_map.update(custom_me_map)

        self.event_bus = EventBusClient()

        # Create OMCI communications channel
        self._omci_cc = OMCI_CC(adapter_agent, self.device_id, self._me_map)
示例#20
0
    def test_topic_filtering(self):

        ebc = EventBusClient(EventBus())

        mock = Mock()
        ebc.subscribe('news', mock)

        ebc.publish('news', 'msg1')
        ebc.publish('alerts', 'msg2')
        ebc.publish('logs', 'msg3')

        self.assertEqual(mock.call_count, 1)
        mock.assert_called_with('news', 'msg1')
示例#21
0
class EventBusPublisher(object):
    def __init__(self, kafka_proxy, config):
        self.kafka_proxy = kafka_proxy
        self.config = config
        self.topic_mappings = config.get('topic_mappings', {})
        self.event_bus = EventBusClient()

    def start(self):
        log.debug('starting')
        self._setup_subscriptions(self.topic_mappings)
        log.info('started')
        return self

    def stop(self):
        log.debug('stopping')
        log.info('stopped')

    def _setup_subscriptions(self, mappings):

        for event_bus_topic, mapping in mappings.iteritems():

            kafka_topic = mapping.get('kafka_topic', None)

            if kafka_topic is None:
                log.error('no-kafka-topic-in-config',
                          event_bus_topic=event_bus_topic,
                          mapping=mapping)
                continue

            self.event_bus.subscribe(
                event_bus_topic,
                # to avoid Python late-binding to the last registered
                # kafka_topic, we force instant binding with the default arg
                lambda _, m, k=kafka_topic: self.forward(k, m))

            log.info('event-to-kafka',
                     kafka_topic=kafka_topic,
                     event_bus_topic=event_bus_topic)

    def forward(self, kafka_topic, msg):

        # convert to JSON string if msg is a protobuf msg
        if isinstance(msg, Message):
            msg = dumps(MessageToDict(msg, True, True))

        self.kafka_proxy.send_message(kafka_topic, msg)
示例#22
0
    def test_unsubscribe(self):

        ebc = EventBusClient(EventBus())
        sub = ebc.subscribe('news', lambda msg, topic: None)
        ebc.unsubscribe(sub)
        self.assertEqual(ebc.list_subscribers(), [])
        self.assertEqual(ebc.list_subscribers('news'), [])
示例#23
0
    def __init__(self,
                 omci_agent,
                 device_id,
                 entity_class,
                 serial_number,
                 logical_device_id,
                 exclusive=True,
                 allow_failure=False,
                 **kwargs):
        """
        Class initialization

        :param omci_agent: (OmciAdapterAgent) OMCI Adapter agent
        :param device_id: (str) ONU Device ID
        :param entity_class: (EntityClass) ME Class to retrieve
        :param entity_id: (int) ME Class instance ID to retrieve
        :param attributes: (list or set) Name of attributes to retrieve
        :param exclusive: (bool) True if this GET request Task exclusively own the
                                 OMCI-CC while running. Default: True
        :param allow_failure: (bool) If true, attempt to get all valid attributes
                                     if the original request receives an error
                                     code of 9 (Attributes failed or unknown).
        """
        super(OmciTestRequest,
              self).__init__(OmciTestRequest.name,
                             omci_agent,
                             device_id,
                             priority=OmciTestRequest.task_priority,
                             exclusive=exclusive)
        self._device = omci_agent.get_device(device_id)
        self._entity_class = entity_class
        self._allow_failure = allow_failure
        self._failed_or_unknown_attributes = set()
        self._results = None
        self._local_deferred = None
        self.device_id = device_id
        self.event_bus = EventBusClient()
        self.lc = None
        self.default_freq = self.default_freq = \
            kwargs.get(OmciTestRequest.DEFAULT_FREQUENCY_KEY,
                       OmciTestRequest.DEFAULT_COLLECTION_FREQUENCY)
        self.serial_number = serial_number
        self.logical_device_id = logical_device_id
        topic = 'omci-rx:{}:{}'.format(self.device_id, 'Test_Result')
        self.msg = self.event_bus.subscribe(topic, self.process_messages)
示例#24
0
    def __init__(self, adapter_agent, device_id, me_map=None, clock=None):
        self.log = structlog.get_logger(device_id=device_id)
        self._adapter_agent = adapter_agent
        self._device_id = device_id
        self._proxy_address = None
        self._enabled = False
        self._extended_messaging = False
        self._me_map = me_map
        if clock is None:
            self.reactor = reactor
        else:
            self.reactor = clock

        # Support 2 levels of priority since only baseline message set supported
        self._tx_tid = [
            OMCI_CC.MIN_OMCI_TX_ID_LOW_PRIORITY,
            OMCI_CC.MIN_OMCI_TX_ID_HIGH_PRIORITY
        ]
        self._tx_request = [
            None, None
        ]  # Tx in progress (timestamp, defer, frame, timeout, retry, delayedCall)
        self._pending = [
            list(), list()
        ]  # pending queue (deferred, tx_frame, timeout, retry)
        self._rx_response = [None, None]

        # Statistics
        self._tx_frames = 0
        self._rx_frames = 0
        self._rx_unknown_tid = 0  # Rx OMCI with no Tx TID match
        self._rx_onu_frames = 0  # Autonomously generated ONU frames
        self._rx_onu_discards = 0  # Autonomously generated ONU unknown message types
        self._rx_timeouts = 0
        self._rx_late = 0  # Frame response received after timeout on Tx
        self._rx_unknown_me = 0  # Number of managed entities Rx without a decode definition
        self._tx_errors = 0  # Exceptions during tx request
        self._consecutive_errors = 0  # Rx & Tx errors in a row, a good RX resets this to 0
        self._reply_min = sys.maxint  # Fastest successful tx -> rx
        self._reply_max = 0  # Longest successful tx -> rx
        self._reply_sum = 0.0  # Total seconds for successful tx->rx (float for average)
        self._max_hp_tx_queue = 0  # Maximum size of high priority tx pending queue
        self._max_lp_tx_queue = 0  # Maximum size of low priority tx pending queue

        self.event_bus = EventBusClient()
示例#25
0
 def __init__(self, adapter_name, adapter_cls):
     self.adapter_name = adapter_name
     self.adapter_cls = adapter_cls
     self.core = registry('core')
     self.adapter = None
     self.adapter_node_proxy = None
     self.root_proxy = self.core.get_proxy('/')
     self._rx_event_subscriptions = {}
     self._tx_event_subscriptions = {}
     self.event_bus = EventBusClient()
     self.log = structlog.get_logger(adapter_name=adapter_name)
示例#26
0
    def test_unsubscribe(self):

        ebc = EventBusClient(EventBus())
        sub = ebc.subscribe('news', lambda msg, topic: None)
        ebc.unsubscribe(sub)
        self.assertEqual(ebc.list_subscribers(), [])
        self.assertEqual(ebc.list_subscribers('news'), [])
示例#27
0
    def test_subscribe(self):

        ebc = EventBusClient()
        sub = ebc.subscribe('news', lambda msg, topic: None)
        self.assertEqual(len(ebc.list_subscribers()), 1)
        self.assertEqual(len(ebc.list_subscribers('news')), 1)
        self.assertEqual(len(ebc.list_subscribers('other')), 0)
示例#28
0
    def test_subscribers_that_unsubscribe_when_called(self):
        # VOL-943 bug fix check
        ebc = EventBusClient(EventBus())

        class UnsubscribeWhenCalled(object):
            def __init__(self):
                self.subscription = ebc.subscribe('news', self.unsubscribe)
                self.called = False

            def unsubscribe(self, _topic, _msg):
                self.called = True
                ebc.unsubscribe(self.subscription)

        ebc1 = UnsubscribeWhenCalled()
        ebc2 = UnsubscribeWhenCalled()
        ebc3 = UnsubscribeWhenCalled()

        ebc.publish('news', 'msg1')

        self.assertTrue(ebc1.called)
        self.assertTrue(ebc2.called)
        self.assertTrue(ebc3.called)
示例#29
0
    def __init__(self, omci_agent, device_id, adapter_agent, custom_me_map,
                 mib_synchronizer_info, mib_db):
        """
        Class initializer

        :param device_id: (str) ONU Device ID
        :param custom_me_map: (dict) Additional/updated ME to add to class map
        """
        self.log = structlog.get_logger(device_id=device_id)

        self._started = False
        self._omci_agent = omci_agent  # OMCI AdapterAgent
        self._device_id = device_id  # ONU Device ID
        self._runner = TaskRunner(device_id)  # OMCI_CC Task runner
        self._deferred = None

        try:
            self._mib_db_in_sync = False
            self.mib_sync = mib_synchronizer_info['state-machine'](
                self._omci_agent, device_id, mib_synchronizer_info['tasks'],
                mib_db)
        except Exception as e:
            self.log.exception('mib-sync-create-failed', e=e)
            raise

        self._state_machines = [self.mib_sync]
        self._custom_me_map = custom_me_map
        self._me_map = omci_entities.entity_id_to_class_map.copy()

        if custom_me_map is not None:
            self._me_map.update(custom_me_map)

        self.event_bus = EventBusClient()

        # Create OMCI communications channel
        self._omci_cc = OMCI_CC(adapter_agent, self.device_id, self._me_map)
示例#30
0
    def test_simple_publish(self):

        ebc = EventBusClient(EventBus())

        mock = Mock()
        ebc.subscribe('news', mock)

        ebc.publish('news', 'message')

        self.assertEqual(mock.call_count, 1)
        mock.assert_called_with('news', 'message')
示例#31
0
    def __init__(self,
                 agent,
                 device_id,
                 mib_sync_tasks,
                 db,
                 states=DEFAULT_STATES,
                 transitions=DEFAULT_TRANSITIONS,
                 initial_state='disabled',
                 timeout_delay=DEFAULT_TIMEOUT_RETRY,
                 audit_delay=DEFAULT_AUDIT_DELAY,
                 resync_delay=DEFAULT_RESYNC_DELAY):
        """
        Class initialization

        :param agent: (OpenOmciAgent) Agent
        :param device_id: (str) ONU Device ID
        :param db: (MibDbVolatileDict) MIB Database
        :param mib_sync_tasks: (dict) Tasks to run
        :param states: (list) List of valid states
        :param transitions: (dict) Dictionary of triggers and state changes
        :param initial_state: (str) Initial state machine state
        :param timeout_delay: (int/float) Number of seconds after a timeout to attempt
                                          a retry (goes back to starting state)
        :param audit_delay: (int) Seconds between MIB audits while in sync. Set to
                                  zero to disable audit. An operator can request
                                  an audit manually by calling 'self.audit_mib'
        :param resync_delay: (int) Seconds in sync before performing a forced MIB
                                   resynchronization
        """
        self.log = structlog.get_logger(device_id=device_id)

        self._agent = agent
        self._device_id = device_id
        self._device = None
        self._database = db
        self._timeout_delay = timeout_delay
        self._audit_delay = audit_delay
        self._resync_delay = resync_delay

        self._upload_task = mib_sync_tasks['mib-upload']
        self._get_mds_task = mib_sync_tasks['get-mds']
        self._audit_task = mib_sync_tasks['mib-audit']
        self._resync_task = mib_sync_tasks['mib-resync']

        self._deferred = None
        self._current_task = None  # TODO: Support multiple running tasks after v.1.3.0 release
        self._task_deferred = None
        self._mib_data_sync = 0
        self._last_mib_db_sync_value = None
        self._device_in_db = False

        self._on_olt_only_diffs = None
        self._on_onu_only_diffs = None
        self._attr_diffs = None

        self._event_bus = EventBusClient()
        self._subscriptions = {  # RxEvent.enum -> Subscription Object
            RxEvent.MIB_Reset: None,
            RxEvent.AVC_Notification: None,
            RxEvent.MIB_Upload: None,
            RxEvent.MIB_Upload_Next: None,
            RxEvent.Create: None,
            RxEvent.Delete: None,
            RxEvent.Set: None
        }
        self._sub_mapping = {
            RxEvent.MIB_Reset: self.on_mib_reset_response,
            RxEvent.AVC_Notification: self.on_avc_notification,
            RxEvent.MIB_Upload: self.on_mib_upload_response,
            RxEvent.MIB_Upload_Next: self.on_mib_upload_next_response,
            RxEvent.Create: self.on_create_response,
            RxEvent.Delete: self.on_delete_response,
            RxEvent.Set: self.on_set_response
        }
        # Statistics and attributes
        # TODO: add any others if it will support problem diagnosis

        # Set up state machine to manage states
        self.machine = Machine(model=self,
                               states=states,
                               transitions=transitions,
                               initial=initial_state,
                               queued=True,
                               name='{}'.format(self.__class__.__name__))
示例#32
0
 def __init__(self):
     self._event_bus_client = EventBusClient()
     self._topic = 'model-change-events'
 def __init__(self, kafka_proxy, config):
     self.kafka_proxy = kafka_proxy
     self.config = config
     self.topic_mappings = config.get('topic_mappings', {})
     self.event_bus = EventBusClient()
     self.subscriptions = None
示例#34
0
    def __init__(self, core, logical_device):
        try:
            self.core = core
            self.local_handler = core.get_local_handler()
            self.logical_device_id = logical_device.id

            self.root_proxy = core.get_proxy('/')
            self.flows_proxy = core.get_proxy(
                '/logical_devices/{}/flows'.format(logical_device.id))
            self.meters_proxy = core.get_proxy(
                '/logical_devices/{}/meters'.format(logical_device.id))
            self.groups_proxy = core.get_proxy(
                '/logical_devices/{}/flow_groups'.format(logical_device.id))
            self.self_proxy = core.get_proxy(
                '/logical_devices/{}'.format(logical_device.id))

            self.flows_proxy.register_callback(
                CallbackType.PRE_UPDATE, self._pre_process_flows)
            self.flows_proxy.register_callback(
                CallbackType.POST_UPDATE, self._flow_table_updated)
            self.groups_proxy.register_callback(
                CallbackType.POST_UPDATE, self._group_table_updated)
            self.self_proxy.register_callback(
                CallbackType.POST_ADD, self._port_added)
            self.self_proxy.register_callback(
                CallbackType.POST_REMOVE, self._port_removed)

            self.port_proxy = {}
            self.port_status_has_changed = {}

            self.event_bus = EventBusClient()
            self.packet_in_subscription = self.event_bus.subscribe(
                topic='packet-in:{}'.format(logical_device.id),
                callback=self.handle_packet_in_event)

            self.log = structlog.get_logger(logical_device_id=logical_device.id)

            self._routes = None
            self._no_flow_changes_required = False
            self._flows_ids_to_add = []
            self._flows_ids_to_remove = []
            self._flows_to_remove = []
            self._flow_with_unknown_meter = dict()

            self.accepts_direct_logical_flows = False
            self.device_id = self.self_proxy.get('/').root_device_id
            device_adapter_type = self.root_proxy.get('/devices/{}'.format(
                self.device_id)).adapter
            device_type = self.root_proxy.get('/device_types/{}'.format(
                device_adapter_type))

            if device_type is not None:
                self.accepts_direct_logical_flows = \
                    device_type.accepts_direct_logical_flows_update

            if self.accepts_direct_logical_flows:

                self.device_adapter_agent = registry(
                    'adapter_loader').get_agent(device_adapter_type).adapter

                self.log.debug('this device accepts direct logical flows',
                               device_adapter_type=device_adapter_type)



        except Exception, e:
            self.log.exception('init-error', e=e)
示例#35
0
    def __init__(self,
                 omci_agent,
                 device_id,
                 adapter_agent,
                 custom_me_map,
                 mib_db,
                 alarm_db,
                 support_classes,
                 clock=None):
        """
        Class initializer

        :param omci_agent: (OpenOMCIAgent) Reference to OpenOMCI Agent
        :param device_id: (str) ONU Device ID
        :param adapter_agent: (AdapterAgent) Adapter agent for ONU
        :param custom_me_map: (dict) Additional/updated ME to add to class map
        :param mib_db: (MibDbApi) MIB Database reference
        :param alarm_db: (MibDbApi) Alarm Table/Database reference
        :param support_classes: (dict) State machines and tasks for this ONU
        """
        self.log = structlog.get_logger(device_id=device_id)

        self._started = False
        self._omci_agent = omci_agent  # OMCI AdapterAgent
        self._device_id = device_id  # ONU Device ID
        self._adapter_agent = adapter_agent
        self._runner = TaskRunner(device_id,
                                  clock=clock)  # OMCI_CC Task runner
        self._deferred = None
        # self._img_download_deferred = None    # deferred of image file download from server
        self._omci_upgrade_deferred = None  # deferred of ONU OMCI upgrading procedure
        self._omci_activate_deferred = None  # deferred of ONU OMCI Softwre Image Activate
        self._img_deferred = None  # deferred returned to caller of do_onu_software_download
        self._first_in_sync = False
        self._first_capabilities = False
        self._timestamp = None
        # self._image_download = None  # (voltha_pb2.ImageDownload)
        self.reactor = clock if clock is not None else reactor

        # OMCI related databases are on a per-agent basis. State machines and tasks
        # are per ONU Vendor
        #
        self._support_classes = support_classes
        self._configuration = None

        try:
            # MIB Synchronization state machine
            self._mib_db_in_sync = False
            mib_synchronizer_info = support_classes.get('mib-synchronizer')
            advertise = mib_synchronizer_info['advertise-events']
            self._mib_sync_sm = mib_synchronizer_info['state-machine'](
                self._omci_agent,
                device_id,
                mib_synchronizer_info['tasks'],
                mib_db,
                advertise_events=advertise)
            # ONU OMCI Capabilities state machine
            capabilities_info = support_classes.get('omci-capabilities')
            advertise = capabilities_info['advertise-events']
            self._capabilities_sm = capabilities_info['state-machine'](
                self._omci_agent,
                device_id,
                capabilities_info['tasks'],
                advertise_events=advertise)
            # ONU Performance Monitoring Intervals state machine
            interval_info = support_classes.get('performance-intervals')
            advertise = interval_info['advertise-events']
            self._pm_intervals_sm = interval_info['state-machine'](
                self._omci_agent,
                device_id,
                interval_info['tasks'],
                advertise_events=advertise)

            # ONU ALARM Synchronization state machine
            self._alarm_db_in_sync = False
            alarm_synchronizer_info = support_classes.get('alarm-synchronizer')
            advertise = alarm_synchronizer_info['advertise-events']
            self._alarm_sync_sm = alarm_synchronizer_info['state-machine'](
                self._omci_agent,
                device_id,
                alarm_synchronizer_info['tasks'],
                alarm_db,
                advertise_events=advertise)
            # State machine of downloading image file from server
            downloader_info = support_classes.get('image_downloader')
            image_upgrader_info = support_classes.get('image_upgrader')
            # image_activate_info = support_classes.get('image_activator')
            advertise = downloader_info['advertise-event']
            # self._img_download_sm = downloader_info['state-machine'](self._omci_agent, device_id,
            #                                                       downloader_info['tasks'],
            #                                                       advertise_events=advertise)
            self._image_agent = ImageAgent(
                self._omci_agent,
                device_id,
                downloader_info['state-machine'],
                downloader_info['tasks'],
                image_upgrader_info['state-machine'],
                image_upgrader_info['tasks'],
                # image_activate_info['state-machine'],
                advertise_events=advertise,
                clock=clock)

            # self._omci_upgrade_sm = image_upgrader_info['state-machine'](device_id, advertise_events=advertise)

        except Exception as e:
            self.log.exception('state-machine-create-failed', e=e)
            raise

        # Put state machines in the order you wish to start them

        self._state_machines = []
        self._on_start_state_machines = [  # Run when 'start()' called
            self._mib_sync_sm,
            self._capabilities_sm,
        ]
        self._on_sync_state_machines = [  # Run after first in_sync event
            self._alarm_sync_sm,
        ]
        self._on_capabilities_state_machines = [  # Run after first capabilities events
            self._pm_intervals_sm
        ]
        self._custom_me_map = custom_me_map
        self._me_map = omci_entities.entity_id_to_class_map.copy()

        if custom_me_map is not None:
            self._me_map.update(custom_me_map)

        self.event_bus = EventBusClient()

        # Create OMCI communications channel
        self._omci_cc = OMCI_CC(adapter_agent,
                                self.device_id,
                                self._me_map,
                                clock=clock)
示例#36
0
 def __init__(self):
     self._event_bus_client = EventBusClient()
     self._topic = 'model-change-events'
示例#37
0
 def setUp(self):
     super(TestEventLogic, self).setUp()
     self.ebc = EventBusClient()
     self.event_mock = Mock()
     self.ebc.subscribe('model-change-events', self.event_mock)
示例#38
0
    def test_predicates(self):

        ebc = EventBusClient(EventBus())

        get_foos = Mock()
        ebc.subscribe('', get_foos, lambda msg: msg.startswith('foo'))

        get_bars = Mock()
        ebc.subscribe('', get_bars, lambda msg: msg.endswith('bar'))

        get_all = Mock()
        ebc.subscribe('', get_all)

        get_none = Mock()
        ebc.subscribe('', get_none, lambda msg: msg.find('zoo') >= 0)

        errored = Mock()
        ebc.subscribe('', errored, lambda msg: 1/0)

        ebc.publish('', 'foo')
        ebc.publish('', 'foobar')
        ebc.publish('', 'bar')

        c = call

        self.assertEqual(get_foos.call_count, 2)
        get_foos.assert_has_calls([c('', 'foo'), c('', 'foobar')])

        self.assertEqual(get_bars.call_count, 2)
        get_bars.assert_has_calls([c('', 'foobar'), c('', 'bar')])

        self.assertEqual(get_all.call_count, 3)
        get_all.assert_has_calls([c('', 'foo'), c('', 'foobar'), c('', 'bar')])

        get_none.assert_not_called()

        errored.assert_not_called()
示例#39
0
    def __init__(self,
                 agent,
                 device_id,
                 alarm_sync_tasks,
                 db,
                 advertise_events=False,
                 states=DEFAULT_STATES,
                 transitions=DEFAULT_TRANSITIONS,
                 initial_state='disabled',
                 timeout_delay=DEFAULT_TIMEOUT_RETRY,
                 audit_delay=DEFAULT_AUDIT_DELAY):
        """
        Class initialization

        :param agent: (OpenOmciAgent) Agent
        :param device_id: (str) ONU Device ID
        :param db: (MibDbApi) MIB/Alarm Database
        :param advertise_events: (bool) Advertise events on OpenOMCI Event Bus
        :param alarm_sync_tasks: (dict) Tasks to run
        :param states: (list) List of valid states
        :param transitions: (dict) Dictionary of triggers and state changes
        :param initial_state: (str) Initial state machine state
        :param timeout_delay: (int/float) Number of seconds after a timeout to attempt
                                          a retry (goes back to starting state)
        :param audit_delay: (int) Seconds between Alarm audits while in sync. Set to
                                  zero to disable audit. An operator can request
                                  an audit manually by calling 'self.audit_alarm'
        """

        self.log = structlog.get_logger(device_id=device_id)

        self._agent = agent
        self._device_id = device_id
        self._device = None
        self._database = db
        self._timeout_delay = timeout_delay
        self._audit_delay = audit_delay
        self._resync_task = alarm_sync_tasks['alarm-resync']
        self._advertise_events = advertise_events
        self._alarm_manager = None
        self._onu_id = None
        self._uni_ports = list()
        self._ani_ports = list()

        self._deferred = None
        self._current_task = None
        self._task_deferred = None
        self._last_alarm_sequence_value = 0
        self._device_in_db = False

        self._event_bus = EventBusClient()
        self._omci_cc_subscriptions = {  # RxEvent.enum -> Subscription Object
            RxEvent.Get_ALARM_Get: None,
            RxEvent.Alarm_Notification: None
        }
        self._omci_cc_sub_mapping = {
            RxEvent.Get_ALARM_Get: self.on_alarm_update_response,
            RxEvent.Alarm_Notification: self.on_alarm_notification
        }

        # Statistics and attributes
        # TODO: add any others if it will support problem diagnosis

        # Set up state machine to manage states
        self.machine = Machine(model=self,
                               states=states,
                               transitions=transitions,
                               initial=initial_state,
                               queued=True,
                               name='{}-{}'.format(self.__class__.__name__,
                                                   device_id))
示例#40
0
    def test_wildcard_topic(self):

        ebc = EventBusClient(EventBus())
        subs = []

        wildcard_sub = Mock()
        subs.append(ebc.subscribe(re.compile(r'.*'), wildcard_sub))

        prefix_sub = Mock()
        subs.append(ebc.subscribe(re.compile(r'ham.*'), prefix_sub))

        contains_sub = Mock()
        subs.append(ebc.subscribe(re.compile(r'.*burg.*'), contains_sub))

        ebc.publish('news', 1)
        ebc.publish('hamsters', 2)
        ebc.publish('hamburgers', 3)
        ebc.publish('nonsense', 4)

        c = call

        self.assertEqual(wildcard_sub.call_count, 4)
        wildcard_sub.assert_has_calls([
            c('news', 1),
            c('hamsters', 2),
            c('hamburgers', 3),
            c('nonsense', 4)])

        self.assertEqual(prefix_sub.call_count, 2)
        prefix_sub.assert_has_calls([
            c('hamsters', 2),
            c('hamburgers', 3)])

        self.assertEqual(contains_sub.call_count, 1)
        contains_sub.assert_has_calls([c('hamburgers', 3)])

        for sub in subs:
            ebc.unsubscribe(sub)

        self.assertEqual(ebc.list_subscribers(), [])
示例#41
0
class OmciTestRequest(Task):
    """
    OpenOMCI Test an OMCI ME Instance Attributes

    Upon completion, the Task deferred callback is invoked with a reference of
    this Task object.

    """
    task_priority = 128
    name = "ONU OMCI Test Task"
    MAX_TABLE_SIZE = 16 * 1024  # Keep get-next logic reasonable
    OPTICAL_GROUP_NAME = 'PON_Optical'
    DEFAULT_COLLECTION_FREQUENCY = 600 * 10  # 10 minutes
    DEFAULT_FREQUENCY_KEY = 'default-collection-frequency'

    def __init__(self,
                 omci_agent,
                 device_id,
                 entity_class,
                 serial_number,
                 logical_device_id,
                 exclusive=True,
                 allow_failure=False,
                 **kwargs):
        """
        Class initialization

        :param omci_agent: (OmciAdapterAgent) OMCI Adapter agent
        :param device_id: (str) ONU Device ID
        :param entity_class: (EntityClass) ME Class to retrieve
        :param entity_id: (int) ME Class instance ID to retrieve
        :param attributes: (list or set) Name of attributes to retrieve
        :param exclusive: (bool) True if this GET request Task exclusively own the
                                 OMCI-CC while running. Default: True
        :param allow_failure: (bool) If true, attempt to get all valid attributes
                                     if the original request receives an error
                                     code of 9 (Attributes failed or unknown).
        """
        super(OmciTestRequest,
              self).__init__(OmciTestRequest.name,
                             omci_agent,
                             device_id,
                             priority=OmciTestRequest.task_priority,
                             exclusive=exclusive)
        self._device = omci_agent.get_device(device_id)
        self._entity_class = entity_class
        self._allow_failure = allow_failure
        self._failed_or_unknown_attributes = set()
        self._results = None
        self._local_deferred = None
        self.device_id = device_id
        self.event_bus = EventBusClient()
        self.lc = None
        self.default_freq = self.default_freq = \
            kwargs.get(OmciTestRequest.DEFAULT_FREQUENCY_KEY,
                       OmciTestRequest.DEFAULT_COLLECTION_FREQUENCY)
        self.serial_number = serial_number
        self.logical_device_id = logical_device_id
        topic = 'omci-rx:{}:{}'.format(self.device_id, 'Test_Result')
        self.msg = self.event_bus.subscribe(topic, self.process_messages)

    def cancel_deferred(self):
        """

        :return: None
        """
        super(OmciTestRequest, self).cancel_deferred()

        d, self._local_deferred = self._local_deferred, None
        try:
            if d is not None and not d.called:
                d.cancel()
        except:
            pass

    @property
    def me_class(self):
        """The OMCI Managed Entity Class associated with this request"""
        return self._entity_class

    @property
    def entity_id(self):
        """The ME Entity ID associated with this request"""
        return self._entity_id

    @property
    def success_code(self):
        """
        Return the OMCI success/reason code for the Get Response.
        """
        if self._results is None:
            return None
        return self._results.fields['omci_message'].fields['success']

    def start_collector(self, callback=None):
        """
                Start the collection loop for an adapter if the frequency > 0

                :param callback: (callable) Function to call to collect PM data
        """
        self.log.info("starting-pm-collection", device_name=self.name)
        if callback is None:
            callback = self.perform_test_omci

        if self.lc is None:
            self.lc = LoopingCall(callback)

        if self.default_freq > 0:
            self.lc.start(interval=self.default_freq / 10)

    def submit_kpis(self, kpi_event):
        """

        :param kpi_event: List of dict.actual event information.
        :return: None
        """
        try:
            assert isinstance(kpi_event, (KpiEvent, KpiEvent2))
            self.event_bus.publish('kpis', kpi_event)
        except Exception as e:
            self.log.exception('failed-kpi-submission', type=type(kpi_event))

    def publish_metrics(self, data, event_name, onu_device_id):
        """

        :param data:  actual test result dict
        :param event_name: Test_result
        :param onu_device_id:  Onu device id
        :return: None
        """
        metric_data = MetricInformation(metadata=MetricMetaData(
            title=OmciTestRequest.OPTICAL_GROUP_NAME,
            ts=arrow.utcnow().float_timestamp,
            logical_device_id=self.logical_device_id,
            serial_no=self.serial_number,
            device_id=onu_device_id,
            context={'events': event_name}),
                                        metrics=data)
        self.log.info('Publish-Test-Result')
        kpi_event = KpiEvent2(type=KpiEventType.slice,
                              ts=arrow.utcnow().float_timestamp,
                              slice_data=[metric_data])

        self.submit_kpis(kpi_event)

    def process_messages(self, topic, msg):
        """

        :param topic: topic name of onu.
        :param msg: actual test result dict
        :return: None
        """
        result_frame = {}
        event_name = topic.split(':')[-1]
        onu_device_id = topic.split(':')[-2]
        frame = msg['rx-response']
        for key, value in (frame.fields['omci_message'].fields).iteritems():
            result_frame[key] = long(value)
        self.publish_metrics(result_frame, event_name, onu_device_id)

    @inlineCallbacks
    def perform_test_omci(self):
        """
        Perform the initial test request
        """
        ani_g_entities = self._device.configuration.ani_g_entities
        ani_g_entities_ids = ani_g_entities.keys() if ani_g_entities \
                                                      is not None else None
        self._entity_id = ani_g_entities_ids[0]

        self.log.info('perform-test',
                      entity_class=self._entity_class,
                      entity_id=self._entity_id)
        try:
            frame = MEFrame(self._entity_class, self._entity_id, []).test()
            result = yield self._device.omci_cc.send(frame)
            if not result.fields['omci_message'].fields['success_code']:
                self.log.info(
                    'Self-Test Submitted Successfully',
                    code=result.fields['omci_message'].fields['success_code'])
            else:
                raise TestFailure('Test Failure: {}'.format(
                    result.fields['omci_message'].fields['success_code']))
        except TimeoutError as e:
            self.deferred.errback(failure.Failure(e))

        except Exception as e:
            self.log.exception('perform-test',
                               e=e,
                               class_id=self._entity_class,
                               entity_id=self._entity_id)
            self.deferred.errback(failure.Failure(e))
示例#42
0
class OMCI_CC(object):
    """ Handle OMCI Communication Channel specifics for Adtran ONUs"""

    _frame_to_event_type = {
        OmciMibResetResponse.message_id: RxEvent.MIB_Reset,
        OmciMibUploadResponse.message_id: RxEvent.MIB_Upload,
        OmciMibUploadNextResponse.message_id: RxEvent.MIB_Upload_Next,
        OmciCreateResponse.message_id: RxEvent.Create,
        OmciDeleteResponse.message_id: RxEvent.Delete,
        OmciSetResponse.message_id: RxEvent.Set,
        OmciGetAllAlarmsResponse.message_id: RxEvent.Get_ALARM_Get,
        OmciGetAllAlarmsNextResponse.message_id: RxEvent.Get_ALARM_Get_Next
    }

    def __init__(self, adapter_agent, device_id, me_map=None,
                 alarm_queue_limit=_MAX_INCOMING_ALARM_MESSAGES,
                 avc_queue_limit=_MAX_INCOMING_ALARM_MESSAGES,
                 test_results_queue_limit=_MAX_INCOMING_TEST_RESULT_MESSAGES):
        self.log = structlog.get_logger(device_id=device_id)
        self._adapter_agent = adapter_agent
        self._device_id = device_id
        self._proxy_address = None
        self._tx_tid = 1
        self._enabled = False
        self._requests = dict()       # Tx ID -> (timestamp, deferred, tx_frame, timeout)
        self._alarm_queue = DeferredQueue(size=alarm_queue_limit)
        self._avc_queue = DeferredQueue(size=avc_queue_limit)
        self._test_results_queue = DeferredQueue(size=test_results_queue_limit)
        self._me_map = me_map

        # Statistics
        self._tx_frames = 0
        self._rx_frames = 0
        self._rx_unknown_tid = 0      # Rx OMCI with no Tx TID match
        self._rx_onu_frames = 0       # Autonomously generated ONU frames
        self._rx_alarm_overflow = 0   # Autonomously generated ONU alarms rx overflow
        self._rx_avc_overflow = 0     # Autonomously generated ONU AVC rx overflow
        self._rx_onu_discards = 0     # Autonomously generated ONU unknown message types
        self._rx_timeouts = 0
        self._rx_unknown_me = 0       # Number of managed entities Rx without a decode definition
        self._tx_errors = 0           # Exceptions during tx request
        self._consecutive_errors = 0  # Rx & Tx errors in a row, a good RX resets this to 0
        self._reply_min = sys.maxint  # Fastest successful tx -> rx
        self._reply_max = 0           # Longest successful tx -> rx
        self._reply_sum = 0.0         # Total seconds for successful tx->rx (float for average)

        self.event_bus = EventBusClient()

        # If a list of custom ME Entities classes were provided, insert them into
        # main class_id to entity map.
        # TODO: If this class becomes hidden from the ONU DA, move this to the OMCI State Machine runner

    def __str__(self):
        return "OMCISupport: {}".format(self._device_id)

    @staticmethod
    def event_bus_topic(device_id, event):
        """
        Get the topic name for a given event Frame Type
        :param device_id: (str) ONU Device ID
        :param event: (OmciCCRxEvents) Type of event
        :return: (str) Topic string
        """
        assert event in OmciCCRxEvents, \
            'Event {} is not an OMCI-CC Rx Event'.format(event.name)

        return 'omci-rx:{}:{}'.format(device_id, event.name)

    @property
    def enabled(self):
        return self._enabled

    @enabled.setter
    def enabled(self, value):
        """
        Enable/disable the OMCI Communications Channel

        :param value: (boolean) True to enable, False to disable
        """
        assert isinstance(value, bool), 'enabled is a boolean'

        if self._enabled != value:
            self._enabled = value
            if self._enabled:
                self._start()
            else:
                self._stop()

    @property
    def tx_frames(self):
        return self._tx_frames

    @property
    def rx_frames(self):
        return self._rx_frames

    @property
    def rx_unknown_tid(self):
        return self._rx_unknown_tid         # Tx TID not found

    @property
    def rx_unknown_me(self):
        return self._rx_unknown_me

    @property
    def rx_onu_frames(self):
        return self._rx_onu_frames

    @property
    def rx_alarm_overflow(self):
        return self._rx_alarm_overflow      # Alarm ONU autonomous overflows

    @property
    def rx_avc_overflow(self):
        return self._rx_avc_overflow        # Attribute Value change autonomous overflows

    @property
    def rx_onu_discards(self):
        return self._rx_onu_discards        # Attribute Value change autonomous overflows

    @property
    def rx_timeouts(self):
        return self._rx_timeouts

    @property
    def tx_errors(self):
        return self._tx_errors

    @property
    def consecutive_errors(self):
        return self._consecutive_errors

    @property
    def reply_min(self):
        return int(round(self._reply_min * 1000.0))     # Milliseconds

    @property
    def reply_max(self):
        return int(round(self._reply_max * 1000.0))     # Milliseconds

    @property
    def reply_average(self):
        avg = self._reply_sum / self._rx_frames if self._rx_frames > 0 else 0.0
        return int(round(avg * 1000.0))     # Milliseconds

    @property
    def get_alarm_message(self):
        """
        Attempt to retrieve and remove an ONU Alarm Message from the ONU
        autonomous message queue.

        TODO: We may want to deprecate this, see TODO comment around line 399 in
              the _request_success() method below

        :return: a Deferred which fires with the next Alarm Frame available in
                 the queue.
        """
        return self._alarm_queue.get()

    @property
    def get_avc_message(self):
        """
        Attempt to retrieve and remove an ONU Attribute Value Change (AVC)
        Message from the ONU autonomous message queue.

        TODO: We may want to deprecate this, see TODO comment around line 399 in
              the _request_success() method below

        :return: a Deferred which fires with the next AVC Frame available in
                 the queue.
        """
        return self._avc_queue.get()

    @property
    def get_test_results(self):
        """
        Attempt to retrieve and remove an ONU Test Results Message from the
        ONU autonomous message queue.

        TODO: We may want to deprecate this, see TODO comment around line 399 in
              the _request_success() method below

        :return: a Deferred which fires with the next Test Results Frame is
                 available in the queue.
        """
        return self._test_results_queue.get()

    def _start(self):
        """
        Start the OMCI Communications Channel
        """
        assert self._enabled, 'Start should only be called if enabled'
        self.flush()

        device = self._adapter_agent.get_device(self._device_id)
        self._proxy_address = device.proxy_address

    def _stop(self):
        """
        Stop the OMCI Communications Channel
        """
        assert not self._enabled, 'Stop should only be called if disabled'
        self.flush()
        self._proxy_address = None

        # TODO: What is best way to clean up any outstanding futures for these queues
        self._alarm_queue = None
        self._avc_queue = None
        self._test_results_queue = None

    def _receive_onu_message(self, rx_frame):
        """ Autonomously generated ONU frame Rx handler"""
        from twisted.internet.defer import QueueOverflow
        self.log.debug('rx-onu-frame', frame_type=type(rx_frame),
                       frame=hexify(str(rx_frame)))

        # TODO: Signal, via defer if Alarm Overflow or just an event?
        msg_type = rx_frame.fields['message_type']

        self._rx_onu_frames += 1

        msg = {TX_REQUEST_KEY: None,
               RX_RESPONSE_KEY: rx_frame}

        if msg_type == EntityOperations.AlarmNotification.value:
            topic = OMCI_CC.event_bus_topic(self._device_id, RxEvent.Alarm_Notification)
            reactor.callLater(0,  self.event_bus.publish, topic, msg)
            try:
                self._alarm_queue.put((rx_frame, arrow.utcnow().float_timestamp))

            except QueueOverflow:
                self._rx_alarm_overflow += 1
                self.log.warn('onu-rx-alarm-overflow', cnt=self._rx_alarm_overflow)

        elif msg_type == EntityOperations.AttributeValueChange.value:
            topic = OMCI_CC.event_bus_topic(self._device_id, RxEvent.AVC_Notification)
            reactor.callLater(0,  self.event_bus.publish, topic, msg)
            try:
                self._alarm_queue.put((rx_frame, arrow.utcnow().float_timestamp))

            except QueueOverflow:
                self._rx_avc_overflow += 1
                self.log.warn('onu-rx-avc-overflow', cnt=self._rx_avc_overflow)

        elif msg_type == EntityOperations.TestResult.value:
            topic = OMCI_CC.event_bus_topic(self._device_id, RxEvent.Test_Result)
            reactor.callLater(0,  self.event_bus.publish, topic, msg)
            try:
                self._test_results_queue.put((rx_frame, arrow.utcnow().float_timestamp))

            except QueueOverflow:
                self.log.warn('onu-rx-test-results-overflow')

        else:
            # TODO: Need to add test results message support
            self.log.warn('onu-unsupported-autonomous-message', type=msg_type)
            self._rx_onu_discards += 1

    def receive_message(self, msg):
        """
        Receive and OMCI message from the proxy channel to the OLT.

        Call this from your ONU Adapter on a new OMCI Rx on the proxy channel
        """
        if self.enabled:
            try:
                now = arrow.utcnow()
                d = None

                # NOTE: Since we may need to do an independent ME map on a per-ONU basis
                #       save the current value of the entity_id_to_class_map, then
                #       replace it with our custom one before decode, and then finally
                #       restore it later. Tried other ways but really made the code messy.

                saved_me_map = omci_entities.entity_id_to_class_map
                omci_entities.entity_id_to_class_map = self._me_map

                try:
                    rx_frame = OmciFrame(msg)
                    rx_tid = rx_frame.fields['transaction_id']

                    if rx_tid == 0:
                        return self._receive_onu_message(rx_frame)

                    # Previously unreachable if this is the very first Rx or we
                    # have been running consecutive errors
                    if self._rx_frames == 0 or self._consecutive_errors != 0:
                        reactor.callLater(0, self._publish_connectivity_event, True)

                    self._rx_frames += 1
                    self._consecutive_errors = 0

                except KeyError as e:
                    # Unknown, Unsupported, or vendor-specific ME. Key is the unknown classID
                    self.log.debug('frame-decode-key-error', msg=hexlify(msg), e=e)
                    rx_frame = self._decode_unknown_me(msg)
                    self._rx_unknown_me += 1
                    rx_tid = rx_frame.fields.get('transaction_id')

                except Exception as e:
                    self.log.exception('frame-decode', msg=hexlify(msg), e=e)
                    return

                finally:
                    omci_entities.entity_id_to_class_map = saved_me_map     # Always restore it.

                try:
                    (ts, d, tx_frame, _) = self._requests.pop(rx_tid)

                    ts_diff = now - arrow.Arrow.utcfromtimestamp(ts)
                    secs = ts_diff.total_seconds()
                    self._reply_sum += secs

                    if secs < self._reply_min:
                        self._reply_min = secs

                    if secs > self._reply_max:
                        self._reply_max = secs

                except KeyError as e:
                    # Possible late Rx on a message that timed-out
                    self._rx_unknown_tid += 1
                    self.log.warn('tx-message-missing', rx_id=rx_tid, msg=hexlify(msg))
                    return

                except Exception as e:
                    self.log.exception('frame-match', msg=hexlify(msg), e=e)
                    if d is not None:
                        return d.errback(failure.Failure(e))
                    return

                # Notify sender of completed request
                reactor.callLater(0, d.callback, rx_frame)

                # Publish Rx event to listeners in a different task
                reactor.callLater(0, self._publish_rx_frame, tx_frame, rx_frame)

            except Exception as e:
                self.log.exception('rx-msg', e=e)

    def _decode_unknown_me(self, msg):
        """
        Decode an ME for an unsupported class ID.  This should only occur for a subset
        of message types (Get, Set, MIB Upload Next, ...) and they should only be
        responses as well.

        There are some times below that are commented out. For VOLTHA 2.0, it is
        expected that any get, set, create, delete for unique (often vendor) MEs
        will be coded by the ONU utilizing it and supplied to OpenOMCI as a
        vendor-specific ME during device initialization.

        :param msg: (str) Binary data
        :return: (OmciFrame) resulting frame
        """
        from struct import unpack

        (tid, msg_type, framing) = unpack('!HBB', msg[0:4])

        assert framing == 0xa, 'Only basic OMCI framing supported at this time'
        msg = msg[4:]

        # TODO: Commented out items below are future work (not expected for VOLTHA v2.0)
        (msg_class, kwargs) = {
            # OmciCreateResponse.message_id: (OmciCreateResponse, None),
            # OmciDeleteResponse.message_id: (OmciDeleteResponse, None),
            # OmciSetResponse.message_id: (OmciSetResponse, None),
            # OmciGetResponse.message_id: (OmciGetResponse, None),
            # OmciGetAllAlarmsNextResponse.message_id: (OmciGetAllAlarmsNextResponse, None),
            OmciMibUploadNextResponse.message_id: (OmciMibUploadNextResponse,
                                                   {
                                                       'entity_class': unpack('!H', msg[0:2])[0],
                                                       'entity_id': unpack('!H', msg[2:4])[0],
                                                       'object_entity_class': unpack('!H', msg[4:6])[0],
                                                       'object_entity_id': unpack('!H', msg[6:8])[0],
                                                       'object_attributes_mask': unpack('!H', msg[8:10])[0],
                                                       'object_data': {
                                                           UNKNOWN_CLASS_ATTRIBUTE_KEY: hexlify(msg[10:-4])
                                                       },
                                                   }),
            # OmciAlarmNotification.message_id: (OmciAlarmNotification, None),
            # OmciAttributeValueChange.message_id: (OmciAttributeValueChange,
            #                                       {
            #                                           'entity_class': unpack('!H', msg[0:2])[0],
            #                                           'entity_id': unpack('!H', msg[2:4])[0],
            #                                           'data': {
            #                                               UNKNOWN_CLASS_ATTRIBUTE_KEY: hexlify(msg[4:-8])
            #                                           },
            #                                       }),
            # OmciTestResult.message_id: (OmciTestResult, None),
        }.get(msg_type, None)

        if msg_class is None:
            raise TypeError('Unsupport Message Type for Unknown Decode: {}',
                            msg_type)

        return OmciFrame(transaction_id=tid, message_type=msg_type,
                         omci_message=msg_class(**kwargs))

    def _publish_rx_frame(self, tx_frame, rx_frame):
        """
        Notify listeners of successful response frame
        :param tx_frame: (OmciFrame) Original request frame
        :param rx_frame: (OmciFrame) Response frame
        """
        if self._enabled and isinstance(rx_frame, OmciFrame):
            frame_type = rx_frame.fields['omci_message'].message_id
            event_type = OMCI_CC._frame_to_event_type.get(frame_type)

            if event_type is not None:
                topic = OMCI_CC.event_bus_topic(self._device_id, event_type)
                msg = {TX_REQUEST_KEY: tx_frame,
                       RX_RESPONSE_KEY: rx_frame}

                self.event_bus.publish(topic=topic, msg=msg)

    def _publish_connectivity_event(self, connected):
        """
        Notify listeners of Rx/Tx connectivity over OMCI
        :param connected: (bool) True if connectivity transitioned from unreachable
                                 to reachable
        """
        if self._enabled:
            topic = OMCI_CC.event_bus_topic(self._device_id,
                                            RxEvent.Connectivity)
            msg = {CONNECTED_KEY: connected}
            self.event_bus.publish(topic=topic, msg=msg)

    def flush(self, max_age=0):
        limit = arrow.utcnow().float_timestamp - max_age
        old = [tid for tid, (ts, _, _, _) in self._requests.iteritems()
               if ts <= limit]

        for tid in old:
            (_, d, _, _) = self._requests.pop(tid)
            if d is not None and not d.called:
                d.cancel()

        self._requests = dict()

        if max_age == 0:
            # Flush autonomous messages (Alarms & AVCs)
            while self._alarm_queue.pending:
                _ = yield self._alarm_queue.get()

            while self._avc_queue.pending:
                _ = yield self._avc_queue.get()

    def _get_tx_tid(self):
        """
        Get the next Transaction ID for a tx.  Note TID=0 is reserved
        for autonomously generated messages from an ONU

        :return: (int) TID
        """
        tx_tid, self._tx_tid = self._tx_tid, self._tx_tid + 1
        if self._tx_tid > MAX_OMCI_TX_ID:
            self._tx_tid = 1

        return tx_tid

    def _request_failure(self, value, tx_tid):
        """
        Handle a transmit failure and/or Rx timeout

        :param value: (Failure) Twisted failure
        :param tx_tid: (int) Associated Tx TID
        """
        if tx_tid in self._requests:
            (_, _, _, timeout) = self._requests.pop(tx_tid)
        else:
            timeout = 0

        if isinstance(value, failure.Failure):
            value.trap(CancelledError)
            self._rx_timeouts += 1
            self._consecutive_errors += 1

            if self._consecutive_errors == 1:
                reactor.callLater(0, self._publish_connectivity_event, False)

            self.log.info('timeout', tx_id=tx_tid, timeout=timeout)
            value = failure.Failure(TimeoutError(timeout, "Deferred"))

        return value

    def _request_success(self, rx_frame):
        """
        Handle transmit success (a matching Rx was received)

        :param rx_frame: (OmciFrame) OMCI response frame with matching TID
        :return: (OmciFrame) OMCI response frame with matching TID
        """
        # At this point, no additional processing is required
        # Continue with Rx Success callbacks.
        return rx_frame

    def send(self, frame, timeout=DEFAULT_OMCI_TIMEOUT):
        """
        Send the OMCI Frame to the ONU via the proxy_channel

        :param frame: (OMCIFrame) Message to send
        :param timeout: (int) Rx Timeout. 0=Forever
        :return: (deferred) A deferred that fires when the response frame is received
                            or if an error/timeout occurs
        """
        self.flush(max_age=MAX_OMCI_REQUEST_AGE)

        assert timeout <= MAX_OMCI_REQUEST_AGE, \
            'Maximum timeout is {} seconds'.format(MAX_OMCI_REQUEST_AGE)
        assert isinstance(frame, OmciFrame), \
            "Invalid frame class '{}'".format(type(frame))

        if not self.enabled or self._proxy_address is None:
            # TODO custom exceptions throughout this code would be helpful
            return fail(result=failure.Failure(Exception('OMCI is not enabled')))

        try:
            tx_tid = frame.fields['transaction_id']
            if tx_tid is None:
                tx_tid = self._get_tx_tid()
                frame.fields['transaction_id'] = tx_tid

            assert tx_tid not in self._requests, 'TX TID {} is already exists'.format(tx_tid)
            assert tx_tid >= 0, 'Invalid Tx TID: {}'.format(tx_tid)

            ts = arrow.utcnow().float_timestamp
            d = defer.Deferred()

            # NOTE: Since we may need to do an independent ME map on a per-ONU basis
            #       save the current value of the entity_id_to_class_map, then
            #       replace it with our custom one before decode, and then finally
            #       restore it later. Tried other ways but really made the code messy.

            saved_me_map = omci_entities.entity_id_to_class_map
            omci_entities.entity_id_to_class_map = self._me_map
            try:
                self._adapter_agent.send_proxied_message(self._proxy_address,
                                                         hexify(str(frame)))
            finally:
                omci_entities.entity_id_to_class_map = saved_me_map

            self._tx_frames += 1
            self._requests[tx_tid] = (ts, d, frame, timeout)

            d.addCallbacks(self._request_success, self._request_failure,
                           errbackArgs=(tx_tid,))

            if timeout > 0:
                d.addTimeout(timeout, reactor)

        except Exception as e:
            self._tx_errors += 1
            self._consecutive_errors += 1

            if self._consecutive_errors == 1:
                reactor.callLater(0, self._publish_connectivity_event, False)

            self.log.exception('send-omci', e=e)
            return fail(result=failure.Failure(e))

        return d

    ###################################################################################
    # MIB Action shortcuts

    def send_mib_reset(self, timeout=DEFAULT_OMCI_TIMEOUT):
        """
        Perform a MIB Reset
        """
        self.log.debug('send-mib-reset')

        frame = OntDataFrame().mib_reset()
        return self.send(frame, timeout)

    def send_mib_upload(self, timeout=DEFAULT_OMCI_TIMEOUT):
        self.log.debug('send-mib-upload')

        frame = OntDataFrame().mib_upload()
        return self.send(frame, timeout)

    def send_mib_upload_next(self, seq_no, timeout=DEFAULT_OMCI_TIMEOUT):
        self.log.debug('send-mib-upload-next')

        frame = OntDataFrame(sequence_number=seq_no).mib_upload_next()
        return self.send(frame, timeout)

    def send_reboot(self, timeout=DEFAULT_OMCI_TIMEOUT):
        """
        Send an ONU Device reboot request (ONU-G ME).

        NOTICE: This method is being deprecated and replaced with a tasks to preform this function
        """
        self.log.debug('send-mib-reboot')

        frame = OntGFrame().reboot()
        return self.send(frame, timeout)

    def send_get_all_alarm(self, alarm_retrieval_mode=0, timeout=DEFAULT_OMCI_TIMEOUT):
        self.log.debug('send_get_alarm')

        frame = OntDataFrame().get_all_alarm(alarm_retrieval_mode)
        return self.send(frame, timeout)

    def send_get_all_alarm_next(self, seq_no, timeout=DEFAULT_OMCI_TIMEOUT):
        self.log.debug('send_get_alarm_next')

        frame = OntDataFrame().get_all_alarm_next(seq_no)
        return self.send(frame, timeout)
示例#43
0
class OnuDeviceEntry(object):
    """
    An ONU Device entry in the MIB
    """
    def __init__(self,
                 omci_agent,
                 device_id,
                 adapter_agent,
                 custom_me_map,
                 mib_db,
                 alarm_db,
                 support_classes,
                 clock=None):
        """
        Class initializer

        :param omci_agent: (OpenOMCIAgent) Reference to OpenOMCI Agent
        :param device_id: (str) ONU Device ID
        :param adapter_agent: (AdapterAgent) Adapter agent for ONU
        :param custom_me_map: (dict) Additional/updated ME to add to class map
        :param mib_db: (MibDbApi) MIB Database reference
        :param alarm_db: (MibDbApi) Alarm Table/Database reference
        :param support_classes: (dict) State machines and tasks for this ONU
        """
        self.log = structlog.get_logger(device_id=device_id)

        self._started = False
        self._omci_agent = omci_agent  # OMCI AdapterAgent
        self._device_id = device_id  # ONU Device ID
        self._adapter_agent = adapter_agent
        self._runner = TaskRunner(device_id,
                                  clock=clock)  # OMCI_CC Task runner
        self._deferred = None
        # self._img_download_deferred = None    # deferred of image file download from server
        self._omci_upgrade_deferred = None  # deferred of ONU OMCI upgrading procedure
        self._omci_activate_deferred = None  # deferred of ONU OMCI Softwre Image Activate
        self._img_deferred = None  # deferred returned to caller of do_onu_software_download
        self._first_in_sync = False
        self._first_capabilities = False
        self._timestamp = None
        # self._image_download = None  # (voltha_pb2.ImageDownload)
        self.reactor = clock if clock is not None else reactor

        # OMCI related databases are on a per-agent basis. State machines and tasks
        # are per ONU Vendor
        #
        self._support_classes = support_classes
        self._configuration = None

        try:
            # MIB Synchronization state machine
            self._mib_db_in_sync = False
            mib_synchronizer_info = support_classes.get('mib-synchronizer')
            advertise = mib_synchronizer_info['advertise-events']
            self._mib_sync_sm = mib_synchronizer_info['state-machine'](
                self._omci_agent,
                device_id,
                mib_synchronizer_info['tasks'],
                mib_db,
                advertise_events=advertise)
            # ONU OMCI Capabilities state machine
            capabilities_info = support_classes.get('omci-capabilities')
            advertise = capabilities_info['advertise-events']
            self._capabilities_sm = capabilities_info['state-machine'](
                self._omci_agent,
                device_id,
                capabilities_info['tasks'],
                advertise_events=advertise)
            # ONU Performance Monitoring Intervals state machine
            interval_info = support_classes.get('performance-intervals')
            advertise = interval_info['advertise-events']
            self._pm_intervals_sm = interval_info['state-machine'](
                self._omci_agent,
                device_id,
                interval_info['tasks'],
                advertise_events=advertise)

            # ONU ALARM Synchronization state machine
            self._alarm_db_in_sync = False
            alarm_synchronizer_info = support_classes.get('alarm-synchronizer')
            advertise = alarm_synchronizer_info['advertise-events']
            self._alarm_sync_sm = alarm_synchronizer_info['state-machine'](
                self._omci_agent,
                device_id,
                alarm_synchronizer_info['tasks'],
                alarm_db,
                advertise_events=advertise)
            # State machine of downloading image file from server
            downloader_info = support_classes.get('image_downloader')
            image_upgrader_info = support_classes.get('image_upgrader')
            # image_activate_info = support_classes.get('image_activator')
            advertise = downloader_info['advertise-event']
            # self._img_download_sm = downloader_info['state-machine'](self._omci_agent, device_id,
            #                                                       downloader_info['tasks'],
            #                                                       advertise_events=advertise)
            self._image_agent = ImageAgent(
                self._omci_agent,
                device_id,
                downloader_info['state-machine'],
                downloader_info['tasks'],
                image_upgrader_info['state-machine'],
                image_upgrader_info['tasks'],
                # image_activate_info['state-machine'],
                advertise_events=advertise,
                clock=clock)

            # self._omci_upgrade_sm = image_upgrader_info['state-machine'](device_id, advertise_events=advertise)

        except Exception as e:
            self.log.exception('state-machine-create-failed', e=e)
            raise

        # Put state machines in the order you wish to start them

        self._state_machines = []
        self._on_start_state_machines = [  # Run when 'start()' called
            self._mib_sync_sm,
            self._capabilities_sm,
        ]
        self._on_sync_state_machines = [  # Run after first in_sync event
            self._alarm_sync_sm,
        ]
        self._on_capabilities_state_machines = [  # Run after first capabilities events
            self._pm_intervals_sm
        ]
        self._custom_me_map = custom_me_map
        self._me_map = omci_entities.entity_id_to_class_map.copy()

        if custom_me_map is not None:
            self._me_map.update(custom_me_map)

        self.event_bus = EventBusClient()

        # Create OMCI communications channel
        self._omci_cc = OMCI_CC(adapter_agent,
                                self.device_id,
                                self._me_map,
                                clock=clock)

    @staticmethod
    def event_bus_topic(device_id, event):
        """
        Get the topic name for a given event for this ONU Device
        :param device_id: (str) ONU Device ID
        :param event: (OnuDeviceEvents) Type of event
        :return: (str) Topic string
        """
        assert event in OnuDeviceEvents, \
            'Event {} is not an ONU Device Event'.format(event.name)
        return 'omci-device:{}:{}'.format(device_id, event.name)

    @property
    def device_id(self):
        return self._device_id

    @property
    def omci_cc(self):
        return self._omci_cc

    @property
    def adapter_agent(self):
        return self._adapter_agent

    @property
    def task_runner(self):
        return self._runner

    @property
    def mib_synchronizer(self):
        """
        Reference to the OpenOMCI MIB Synchronization state machine for this ONU
        """
        return self._mib_sync_sm

    @property
    def omci_capabilities(self):
        """
        Reference to the OpenOMCI OMCI Capabilities state machine for this ONU
        """
        return self._capabilities_sm

    @property
    def pm_intervals_state_machine(self):
        """
        Reference to the OpenOMCI PM Intervals state machine for this ONU
        """
        return self._pm_intervals_sm

    def set_pm_config(self, pm_config):
        """
        Set PM interval configuration

        :param pm_config: (OnuPmIntervalMetrics) PM Interval configuration
        """
        self._pm_intervals_sm.set_pm_config(pm_config)

    @property
    def timestamp(self):
        """Pollable Metrics last collected timestamp"""
        return self._timestamp

    @timestamp.setter
    def timestamp(self, value):
        self._timestamp = value

    @property
    def alarm_synchronizer(self):
        """
        Reference to the OpenOMCI Alarm Synchronization state machine for this ONU
        """
        return self._alarm_sync_sm

    @property
    def active(self):
        """
        Is the ONU device currently active/running
        """
        return self._started

    @property
    def custom_me_map(self):
        """ Vendor-specific Managed Entity Map for this vendor's device"""
        return self._custom_me_map

    @property
    def me_map(self):
        """ Combined ME and Vendor-specific Managed Entity Map for this device"""
        return self._me_map

    def _cancel_deferred(self):
        d, self._deferred = self._deferred, None
        try:
            if d is not None and not d.called:
                d.cancel()
        except:
            pass

    @property
    def mib_db_in_sync(self):
        return self._mib_db_in_sync

    @mib_db_in_sync.setter
    def mib_db_in_sync(self, value):
        if self._mib_db_in_sync != value:
            # Save value
            self._mib_db_in_sync = value

            # Start up other state machines if needed
            if self._first_in_sync:
                self.first_in_sync_event()

            # Notify any event listeners
            topic = OnuDeviceEntry.event_bus_topic(
                self.device_id, OnuDeviceEvents.MibDatabaseSyncEvent)
            msg = {
                IN_SYNC_KEY: self._mib_db_in_sync,
                LAST_IN_SYNC_KEY: self.mib_synchronizer.last_mib_db_sync
            }
            self.event_bus.publish(topic=topic, msg=msg)

    @property
    def alarm_db_in_sync(self):
        return self._alarm_db_in_sync

    @alarm_db_in_sync.setter
    def alarm_db_in_sync(self, value):
        if self._alarm_db_in_sync != value:
            # Save value
            self._alarm_db_in_sync = value

            # Start up other state machines if needed
            if self._first_in_sync:
                self.first_in_sync_event()

            # Notify any event listeners
            topic = OnuDeviceEntry.event_bus_topic(
                self.device_id, OnuDeviceEvents.AlarmDatabaseSyncEvent)
            msg = {IN_SYNC_KEY: self._alarm_db_in_sync}
            self.event_bus.publish(topic=topic, msg=msg)

    @property
    def configuration(self):
        """
        Get the OMCI Configuration object for this ONU.  This is a class that provides some
        common database access functions for ONU capabilities and read-only configuration values.

        :return: (OnuConfiguration)
        """
        return self._configuration

    @property
    def image_agent(self):
        return self._image_agent

    # @property
    # def image_download(self):
    #     return self._image_download

    def start(self):
        """
        Start the ONU Device Entry state machines
        """
        self.log.debug('OnuDeviceEntry.start', previous=self._started)
        if self._started:
            return

        self._started = True
        self._omci_cc.enabled = True
        self._first_in_sync = True
        self._first_capabilities = True
        self._runner.start()
        self._configuration = OnuConfiguration(self._omci_agent,
                                               self._device_id)

        # Start MIB Sync and other state machines that can run before the first
        # MIB Synchronization event occurs. Start 'later' so that any
        # ONU Device, OMCI DB, OMCI Agent, and others are fully started before
        # performing the start.

        self._state_machines = []

        def start_state_machines(machines):
            for sm in machines:
                self._state_machines.append(sm)
                sm.start()

        self._deferred = reactor.callLater(0, start_state_machines,
                                           self._on_start_state_machines)
        # Notify any event listeners
        self._publish_device_status_event()

    def stop(self):
        """
        Stop the ONU Device Entry state machines
        """
        if not self._started:
            return

        self._started = False
        self._cancel_deferred()
        self._omci_cc.enabled = False

        # Halt MIB Sync and other state machines
        for sm in self._state_machines:
            sm.stop()

        self._state_machines = []

        # Stop task runner
        self._runner.stop()

        # Notify any event listeners
        self._publish_device_status_event()

    def first_in_sync_event(self):
        """
        This event is called on the first MIB synchronization event after
        OpenOMCI has been started. It is responsible for starting any
        other state machine and to initiate an ONU Capabilities report
        """
        if self._first_in_sync:
            self._first_in_sync = False

            # Start up the ONU Capabilities task
            self._configuration.reset()

            # Insure that the ONU-G Administrative lock is disabled
            def failure(reason):
                self.log.error('disable-admin-state-lock', reason=reason)

            frame = OntGFrame(attributes={'administrative_state': 0}).set()
            task = OmciModifyRequest(self._omci_agent, self.device_id, frame)
            self.task_runner.queue_task(task).addErrback(failure)

            # Start up any other remaining OpenOMCI state machines
            def start_state_machines(machines):
                for sm in machines:
                    self._state_machines.append(sm)
                    reactor.callLater(0, sm.start)

            self._deferred = reactor.callLater(0, start_state_machines,
                                               self._on_sync_state_machines)

            # if an ongoing upgrading is not accomplished, restart it
            if self._img_deferred is not None:
                self._image_agent.onu_bootup()

    def first_in_capabilities_event(self):
        """
        This event is called on the first capabilities event after
        OpenOMCI has been started. It is responsible for starting any
        other state machine. These are often state machines that have tasks
        that are dependent upon knowing if various MEs are supported
        """
        if self._first_capabilities:
            self._first_capabilities = False

            # Start up any other remaining OpenOMCI state machines
            def start_state_machines(machines):
                for sm in machines:
                    self._state_machines.append(sm)
                    reactor.callLater(0, sm.start)

            self._deferred = reactor.callLater(
                0, start_state_machines, self._on_capabilities_state_machines)

    # def __on_omci_download_success(self, image_download):
    #     self.log.debug("__on_omci_download_success", image=image_download)
    #     self._omci_upgrade_deferred = None
    #     # self._ret_deferred = None
    #     self._omci_activate_deferred = self._image_agent.activate_onu_image(image_download.name)
    #     self._omci_activate_deferred.addCallbacks(self.__on_omci_image_activate_success,
    #                                               self.__on_omci_image_activate_fail, errbackArgs=(image_name,))
    #     return image_name

    # def __on_omci_download_fail(self, fail, image_name):
    #     self.log.debug("__on_omci_download_fail", failure=fail, image_name=image_name)
    #     self.reactor.callLater(0, self._img_deferred.errback, fail)
    #     self._omci_upgrade_deferred = None
    #     self._img_deferred = None

    def __on_omci_image_activate_success(self, image_name):
        self.log.debug("__on_omci_image_activate_success",
                       image_name=image_name)
        self._omci_activate_deferred = None
        self._img_deferred.callback(image_name)
        return image_name

    def __on_omci_image_activate_fail(self, fail, image_name):
        self.log.debug("__on_omci_image_activate_fail",
                       faile=fail,
                       image_name=image_name)
        self._omci_activate_deferred = None
        self._img_deferred.errback(fail)

    def _publish_device_status_event(self):
        """
        Publish the ONU Device start/start status.
        """
        topic = OnuDeviceEntry.event_bus_topic(
            self.device_id, OnuDeviceEvents.DeviceStatusEvent)
        msg = {ACTIVE_KEY: self._started}
        self.event_bus.publish(topic=topic, msg=msg)

    def publish_omci_capabilities_event(self):
        """
        Publish the ONU Device start/start status.
        """
        if self.first_in_capabilities_event:
            self.first_in_capabilities_event()

        topic = OnuDeviceEntry.event_bus_topic(
            self.device_id, OnuDeviceEvents.OmciCapabilitiesEvent)
        msg = {
            SUPPORTED_MESSAGE_ENTITY_KEY:
            self.omci_capabilities.supported_managed_entities,
            SUPPORTED_MESSAGE_TYPES_KEY:
            self.omci_capabilities.supported_message_types
        }
        self.event_bus.publish(topic=topic, msg=msg)

    def delete(self):
        """
        Stop the ONU Device's state machine and remove the ONU, and any related
        OMCI state information from the OpenOMCI Framework
        """
        self.stop()
        self.mib_synchronizer.delete()

        # OpenOMCI cleanup
        if self._omci_agent is not None:
            self._omci_agent.remove_device(self._device_id, cleanup=True)

    def query_mib(self, class_id=None, instance_id=None, attributes=None):
        """
        Get MIB database information.

        This method can be used to request information from the database to the detailed
        level requested

        :param class_id:  (int) Managed Entity class ID
        :param instance_id: (int) Managed Entity instance
        :param attributes: (list or str) Managed Entity instance's attributes

        :return: (dict) The value(s) requested. If class/inst/attribute is
                        not found, an empty dictionary is returned
        :raises DatabaseStateError: If the database is not enabled
        """
        self.log.debug('query',
                       class_id=class_id,
                       instance_id=instance_id,
                       attributes=attributes)

        return self.mib_synchronizer.query_mib(class_id=class_id,
                                               instance_id=instance_id,
                                               attributes=attributes)

    def query_mib_single_attribute(self, class_id, instance_id, attribute):
        """
        Get MIB database information for a single specific attribute

        This method can be used to request information from the database to the detailed
        level requested

        :param class_id:  (int) Managed Entity class ID
        :param instance_id: (int) Managed Entity instance
        :param attribute: (str) Managed Entity instance's attribute

        :return: (varies) The value requested. If class/inst/attribute is
                          not found, None is returned
        :raises DatabaseStateError: If the database is not enabled
        """
        self.log.debug('query-single',
                       class_id=class_id,
                       instance_id=instance_id,
                       attributes=attribute)
        assert isinstance(attribute, basestring), \
            'Only a single attribute value can be retrieved'

        entry = self.mib_synchronizer.query_mib(class_id=class_id,
                                                instance_id=instance_id,
                                                attributes=attribute)

        return entry[attribute] if attribute in entry else None

    def query_alarm_table(self, class_id=None, instance_id=None):
        """
        Get Alarm information

        This method can be used to request information from the alarm database to
        the detailed level requested

        :param class_id:  (int) Managed Entity class ID
        :param instance_id: (int) Managed Entity instance

        :return: (dict) The value(s) requested. If class/inst/attribute is
                        not found, an empty dictionary is returned
        :raises DatabaseStateError: If the database is not enabled
        """
        self.log.debug('query', class_id=class_id, instance_id=instance_id)

        return self.alarm_synchronizer.query_mib(class_id=class_id,
                                                 instance_id=instance_id)

    def reboot(self,
               flags=RebootFlags.Reboot_Unconditionally,
               timeout=OmciRebootRequest.DEFAULT_REBOOT_TIMEOUT):
        """
        Request a reboot of the ONU

        :param flags: (RebootFlags) Reboot condition
        :param timeout: (int) Reboot task priority
        :return: (deferred) Fires upon completion or error
        """
        assert self.active, 'This device is not active'

        return self.task_runner.queue_task(
            OmciRebootRequest(self._omci_agent,
                              self.device_id,
                              flags=flags,
                              timeout=timeout))

    # def get_imagefile(self, local_name, local_dir, remote_url=None):
    #     """
    #     Return a Deferred that will be triggered if the file is locally available
    #     or downloaded successfully
    #     """
    #     self.log.info('start download from {}'.format(remote_url))

    #     # for debug purpose, start runner here to queue downloading task
    #     # self._runner.start()

    #     return self._image_agent.get_image(self._image_download)

    def do_onu_software_download(self, image_dnld):
        """
        image_dnld: (ImageDownload)
        : Return a Deferred that will be triggered when upgrading results in success or failure
        """
        self.log.debug('do_onu_software_download')
        image_download = deepcopy(image_dnld)
        # self._img_download_deferred = self._image_agent.get_image(self._image_download)
        # self._img_download_deferred.addCallbacks(self.__on_download_success, self.__on_download_fail, errbackArgs=(self._image_download,))
        # self._ret_deferred = defer.Deferred()
        # return self._ret_deferred
        return self._image_agent.get_image(image_download)

    # def do_onu_software_switch(self):
    def do_onu_image_activate(self, image_dnld_name):
        """
        Return a Deferred that will be triggered when switching software image results in success or failure
        """
        self.log.debug('do_onu_image_activate')
        if self._img_deferred is None:
            self._img_deferred = defer.Deferred()
            self._omci_upgrade_deferred = self._image_agent.onu_omci_download(
                image_dnld_name)
            self._omci_upgrade_deferred.addCallbacks(
                self.__on_omci_image_activate_success,
                self.__on_omci_image_activate_fail,
                errbackArgs=(image_dnld_name, ))
        return self._img_deferred

    def cancel_onu_software_download(self, image_name):
        self._image_agent.cancel_download_image(image_name)
        self._image_agent.cancel_upgrade_onu()
        if self._img_deferred and not self._img_deferred.called:
            self._img_deferred.cancel()
        self._img_deferred = None
        # self._image_download = None

    def get_image_download_status(self, image_name):
        return self._image_agent.get_image_status(image_name)
示例#44
0
    def test_multiple_subscribers(self):

        ebc = EventBusClient(EventBus())

        mock1 = Mock()
        ebc.subscribe('news', mock1)

        mock2 = Mock()
        ebc.subscribe('alerts', mock2)

        mock3 = Mock()
        ebc.subscribe('logs', mock3)

        mock4 = Mock()
        ebc.subscribe('logs', mock4)

        ebc.publish('news', 'msg1')
        ebc.publish('alerts', 'msg2')
        ebc.publish('logs', 'msg3')

        self.assertEqual(mock1.call_count, 1)
        mock1.assert_called_with('news', 'msg1')

        self.assertEqual(mock2.call_count, 1)
        mock2.assert_called_with('alerts', 'msg2')

        self.assertEqual(mock3.call_count, 1)
        mock3.assert_called_with('logs', 'msg3')

        self.assertEqual(mock4.call_count, 1)
        mock4.assert_called_with('logs', 'msg3')