Esempio n. 1
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)
Esempio n. 2
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)
Esempio n. 3
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)
Esempio n. 4
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)
Esempio n. 5
0
 def setUp(self):
     # defer.setDebugging(True)
     self.runner = TaskRunner(DEVICE_ID)
Esempio n. 6
0
class TestTaskRunner(TestCase):
    """
    Test the Task Runner Object
    """
    def setUp(self):
        # defer.setDebugging(True)
        self.runner = TaskRunner(DEVICE_ID)

    def tearDown(self):
        r, self.runner = self.runner, None
        r.stop()

    def test_default_init(self):
        self.assertFalse(self.runner.active)
        self.assertEqual(self.runner.pending_tasks, 0)
        self.assertEqual(self.runner.running_tasks, 0)
        self.assertEqual(self.runner.successful_tasks_completed, 0)
        self.assertEqual(self.runner.failed_tasks, 0)

    def test_start_stop(self):
        self.assertFalse(self.runner.active)

        self.runner.start()
        self.assertTrue(self.runner.active)

        self.runner.stop()
        self.assertFalse(self.runner.active)

    def unexpected_error(self, _failure):
        self.assertEqual('Should not be here, expected success', _failure)

    def unexpected_success(self, _results):
        self.assertEqual('Should not be here, expected a failure', _results)

    def test_simple_task_init(self):
        t = SimpleTask(None,
                       DEVICE_ID,
                       exclusive=True,
                       priority=0,
                       success=True,
                       value=0,
                       delay=0)

        self.assertEqual(t.priority, 0)
        self.assertGreater(t.task_id, 0)
        self.assertTrue(t.exclusive)
        self.assertFalse(t.deferred.called)

    def test_task_defaults_and_bound(self):
        # Make sure no one has changed some declared defaults/max/min values
        from voltha.extensions.omci.tasks.task import Task
        self.assertEqual(128, Task.DEFAULT_PRIORITY)
        self.assertEqual(0, Task.MIN_PRIORITY)
        self.assertEqual(255, Task.MAX_PRIORITY)
        self.assertEqual(10, Task.DEFAULT_WATCHDOG_SECS)
        self.assertEqual(3, Task.MIN_WATCHDOG_SECS)
        self.assertEqual(60, Task.MAX_WATCHDOG_SECS)

    @raises(AssertionError)
    def test_task_priority_min(self):
        from voltha.extensions.omci.tasks.task import Task
        _ = SimpleTask(None, DEVICE_ID, priority=Task.MIN_PRIORITY - 1)

    @raises(AssertionError)
    def test_task_priority_max(self):
        from voltha.extensions.omci.tasks.task import Task
        _ = SimpleTask(None, DEVICE_ID, priority=Task.MAX_PRIORITY + 1)

    @raises(AssertionError)
    def test_task_watchdog_min(self):
        from voltha.extensions.omci.tasks.task import Task
        _ = SimpleTask(None,
                       DEVICE_ID,
                       watchdog_timeout=Task.MIN_WATCHDOG_SECS - 0.000001)

    @raises(AssertionError)
    def test_task_watchdog_max(self):
        from voltha.extensions.omci.tasks.task import Task
        _ = SimpleTask(None,
                       DEVICE_ID,
                       watchdog_timeout=Task.MAX_WATCHDOG_SECS + 0.000001)

    @deferred(timeout=5)
    def test_simple_success(self):
        expected_result = 123

        t = SimpleTask(None,
                       DEVICE_ID,
                       exclusive=True,
                       priority=0,
                       success=True,
                       value=expected_result,
                       delay=0)

        d = self.runner.queue_task(t)
        self.assertEqual(self.runner.pending_tasks, 1)
        self.assertEqual(self.runner.running_tasks, 0)
        self.runner.start()

        def check_results(results):
            self.assertEqual(results, expected_result)
            self.assertEqual(self.runner.pending_tasks, 0)
            self.assertEqual(self.runner.running_tasks, 0)
            self.assertEqual(self.runner.successful_tasks_completed, 1)
            self.assertEqual(self.runner.failed_tasks, 0)
            self.assertTrue(self.runner.active)
            return results

        d.addCallbacks(check_results, self.unexpected_error)
        return d

    @raises(Exception)
    @deferred(timeout=5)
    def test_simple_failure(self):
        self.expected_failure = Exception('Testing a task failure')

        t = SimpleTask(None,
                       DEVICE_ID,
                       exclusive=True,
                       priority=0,
                       success=False,
                       value=self.expected_failure,
                       delay=0)

        d = self.runner.queue_task(t)
        self.assertEqual(self.runner.pending_tasks, 1)
        self.assertEqual(self.runner.running_tasks, 0)
        self.runner.start()

        def expected_failure(failure):
            self.assertEqual(failure, self.expected_failure)
            self.assertEqual(self.runner.pending_tasks, 0)
            self.assertEqual(self.runner.running_tasks, 0)
            self.assertEqual(self.runner.successful_tasks_completed, 0)
            self.assertEqual(self.runner.failed_tasks, 1)
            self.assertTrue(self.runner.active)
            return failure

        d.addCallbacks(self.unexpected_success, expected_failure)
        return d

    @deferred(timeout=5)
    def test_priority(self):
        self.last_value_set = 0

        t1 = SimpleTask(None,
                        DEVICE_ID,
                        exclusive=True,
                        priority=1,
                        success=True,
                        value=1,
                        delay=0)

        t2 = SimpleTask(
            None,
            DEVICE_ID,
            exclusive=True,
            priority=2,  # Should finish first
            success=True,
            value=2,
            delay=0)

        d1 = self.runner.queue_task(t1)
        d2 = self.runner.queue_task(t2)

        def set_last_value(results):
            self.last_value_set = results

        d1.addCallbacks(set_last_value, self.unexpected_error)
        d2.addCallbacks(set_last_value, self.unexpected_error)

        self.assertEqual(self.runner.pending_tasks, 2)
        self.assertEqual(self.runner.running_tasks, 0)

        d = defer.gatherResults([d1, d2], consumeErrors=True)

        def check_results(_):
            self.assertEqual(self.last_value_set, 1)
            self.assertEqual(self.runner.pending_tasks, 0)
            self.assertEqual(self.runner.running_tasks, 0)
            self.assertEqual(self.runner.successful_tasks_completed, 2)

        d.addCallbacks(check_results, self.unexpected_error)

        self.runner.start()
        return d

    @inlineCallbacks
    def check_that_t1_t2_running_and_last_is_not(self, results):
        from common.utils.asleep import asleep
        yield asleep(0.1)

        self.assertEqual(self.runner.pending_tasks, 1)
        self.assertEqual(self.runner.running_tasks, 2)
        self.assertEqual(self.runner.successful_tasks_completed, 1)

        returnValue(results)

    @deferred(timeout=10)
    def test_concurrent(self):
        blocker = SimpleTask(None,
                             DEVICE_ID,
                             exclusive=True,
                             priority=10,
                             success=True,
                             value=1,
                             delay=0.5)

        t1 = SimpleTask(None,
                        DEVICE_ID,
                        exclusive=False,
                        priority=9,
                        success=True,
                        value=1,
                        delay=2)

        t2 = SimpleTask(None,
                        DEVICE_ID,
                        exclusive=False,
                        priority=9,
                        success=True,
                        value=1,
                        delay=2)

        last = SimpleTask(None,
                          DEVICE_ID,
                          exclusive=True,
                          priority=8,
                          success=True,
                          value=1,
                          delay=0)

        d0 = self.runner.queue_task(blocker)
        d0.addCallbacks(self.check_that_t1_t2_running_and_last_is_not,
                        self.unexpected_error)

        d1 = self.runner.queue_task(t1)
        d2 = self.runner.queue_task(t2)
        d3 = self.runner.queue_task(last)

        self.assertEqual(self.runner.pending_tasks, 4)
        self.assertEqual(self.runner.running_tasks, 0)

        d = defer.gatherResults([d0, d1, d2, d3], consumeErrors=True)

        def check_final_results(_):
            self.assertEqual(self.runner.pending_tasks, 0)
            self.assertEqual(self.runner.running_tasks, 0)
            self.assertEqual(self.runner.successful_tasks_completed, 4)
            self.assertEqual(self.runner.failed_tasks, 0)

        d.addCallbacks(check_final_results, self.unexpected_error)

        self.runner.start()
        return d

    @raises(CancelledError)
    @deferred(timeout=2)
    def test_cancel_queued(self):
        t = SimpleTask(None,
                       DEVICE_ID,
                       exclusive=True,
                       priority=9,
                       success=True,
                       value=1,
                       delay=0)

        d = self.runner.queue_task(t)
        self.assertEqual(self.runner.pending_tasks, 1)
        self.assertEqual(self.runner.running_tasks, 0)

        self.runner.cancel_task(t.task_id)
        self.assertEqual(self.runner.pending_tasks, 0)
        self.assertEqual(self.runner.running_tasks, 0)
        return d

    @raises(CancelledError)
    @deferred(timeout=2)
    def test_task_stop_queued(self):
        t = SimpleTask(None,
                       DEVICE_ID,
                       exclusive=True,
                       priority=9,
                       success=True,
                       value=1,
                       delay=0)

        d = self.runner.queue_task(t)
        self.assertEqual(self.runner.pending_tasks, 1)
        self.assertEqual(self.runner.running_tasks, 0)

        t.stop()
        self.assertEqual(self.runner.pending_tasks, 0)
        self.assertEqual(self.runner.running_tasks, 0)
        return d

    def test_task_stop_not_queued(self):
        t = SimpleTask(None,
                       DEVICE_ID,
                       exclusive=True,
                       priority=9,
                       success=True,
                       value=1,
                       delay=0)

        self.assertEqual(self.runner.pending_tasks, 0)
        self.assertEqual(self.runner.running_tasks, 0)

        t.stop()
        self.assertFalse(t.running)

    @deferred(timeout=3)
    def test_task_runner_cancel_running(self):
        # Both task run in parallel but t1 will finish first and
        # will request t2 to terminate by calling the TaskRunner's
        # cancel task method
        t1 = SimpleTask(None,
                        DEVICE_ID,
                        exclusive=False,
                        priority=9,
                        success=True,
                        value=1,
                        delay=0.5)
        t2 = SimpleTask(None,
                        DEVICE_ID,
                        exclusive=False,
                        priority=9,
                        success=True,
                        value=1,
                        delay=200)

        d1 = self.runner.queue_task(t1)
        d2 = self.runner.queue_task(t2)

        self.assertEqual(self.runner.pending_tasks, 2)
        self.assertEqual(self.runner.running_tasks, 0)

        def kill_task_t2(_, task_2_id):
            # Called on successful completion of task t1
            self.assertIsInstance(task_2_id, int)
            self.assertEqual(self.runner.pending_tasks, 0)
            self.assertEqual(self.runner.running_tasks, 1)

            # Cancel task runner and t2 task ID
            self.runner.cancel_task(task_2_id)
            self.assertEqual(self.runner.running_tasks, 0)

        d1.addCallbacks(kill_task_t2,
                        self.unexpected_error,
                        callbackArgs=[t2.task_id])

        def expected_error(failure):
            self.assertTrue(isinstance(failure.value, CancelledError))
            self.assertEqual(self.runner.pending_tasks, 0)
            self.assertEqual(self.runner.running_tasks, 0)
            self.assertEqual(self.runner.successful_tasks_completed, 1)
            self.assertEqual(self.runner.failed_tasks, 1)

        # T2 should not finish successfully, should get a cancel error
        d2.addCallbacks(self.unexpected_success, expected_error)

        # Run it
        self.runner.start()
        return defer.gatherResults([d1, d2], consumeErrors=True)

    @deferred(timeout=3)
    def test_task_stop_running(self):
        # Run two tasks where T1 completes first and requests T2 to be
        # canceled by calling T2's stop method

        t1 = SimpleTask(None,
                        DEVICE_ID,
                        exclusive=False,
                        priority=9,
                        success=True,
                        value=1,
                        delay=0.5)
        t2 = SimpleTask(None,
                        DEVICE_ID,
                        exclusive=False,
                        priority=9,
                        success=True,
                        value=1,
                        delay=200)

        d1 = self.runner.queue_task(t1)
        d2 = self.runner.queue_task(t2)

        self.assertEqual(self.runner.pending_tasks, 2)
        self.assertEqual(self.runner.running_tasks, 0)

        def kill_task_t2(_, task_2):
            # Called on successful completion of task t1
            self.assertIsInstance(task_2, SimpleTask)
            self.assertEqual(self.runner.pending_tasks, 0)
            self.assertEqual(self.runner.running_tasks, 1)

            # Cancel by telling the task to stop itself
            task_2.stop()
            self.assertEqual(self.runner.running_tasks, 0)

        d1.addCallbacks(kill_task_t2, self.unexpected_error, callbackArgs=[t2])

        def expected_error(failure):
            self.assertTrue(isinstance(failure.value, CancelledError))
            self.assertEqual(self.runner.pending_tasks, 0)
            self.assertEqual(self.runner.running_tasks, 0)
            self.assertEqual(self.runner.successful_tasks_completed, 1)
            self.assertEqual(self.runner.failed_tasks, 1)

        # T2 should not finish successfully, should get a cancel error
        d2.addCallbacks(self.unexpected_success, expected_error)

        # Run it
        self.runner.start()
        return defer.gatherResults([d1, d2], consumeErrors=True)

    @deferred(timeout=3)
    def test_task_cancel_not_queued(self):
        t = SimpleTask(None,
                       DEVICE_ID,
                       exclusive=True,
                       priority=9,
                       success=True,
                       value=1,
                       delay=0)

        self.assertEqual(self.runner.pending_tasks, 0)
        self.assertEqual(self.runner.running_tasks, 0)

        def expected_error(failure):
            self.assertTrue(isinstance(failure.value, CancelledError))
            self.assertEqual(self.runner.pending_tasks, 0)
            self.assertEqual(self.runner.running_tasks, 0)
            self.assertEqual(self.runner.successful_tasks_completed, 0)
            self.assertEqual(self.runner.failed_tasks, 0)
            # self.fail(msg='made it here')    # Uncomment to verify called

        t.deferred.addCallbacks(self.unexpected_success, expected_error)

        self.runner.start()
        t.deferred.cancel()
        self.assertFalse(t.running)
        return t.deferred

    @deferred(timeout=3)
    def test_task_deferred_cancel_running(self):
        # Run two tasks where T1 completes first and requests T2 to be
        # canceled by doing a 'cancel' on T2's deferred

        t1 = SimpleTask(None,
                        DEVICE_ID,
                        exclusive=False,
                        priority=9,
                        success=True,
                        value=1,
                        delay=0.5)
        t2 = SimpleTask(None,
                        DEVICE_ID,
                        exclusive=False,
                        priority=9,
                        success=True,
                        value=1,
                        delay=200)

        d1 = self.runner.queue_task(t1)
        d2 = self.runner.queue_task(t2)

        self.assertEqual(self.runner.pending_tasks, 2)
        self.assertEqual(self.runner.running_tasks, 0)

        def kill_task_t2(_, deferred_2):
            # Called on successful completion of task t1
            self.assertIsInstance(deferred_2, defer.Deferred)
            self.assertEqual(self.runner.pending_tasks, 0)
            self.assertEqual(self.runner.running_tasks, 1)

            # Cancel the deferred for T2
            deferred_2.cancel()
            self.assertEqual(self.runner.running_tasks, 0)

        d1.addCallbacks(kill_task_t2,
                        self.unexpected_error,
                        callbackArgs=[t2.deferred])

        def expected_error(failure):
            self.assertTrue(isinstance(failure.value, CancelledError))
            self.assertEqual(self.runner.pending_tasks, 0)
            self.assertEqual(self.runner.running_tasks, 0)
            self.assertEqual(self.runner.successful_tasks_completed, 1)
            self.assertEqual(self.runner.failed_tasks, 1)
            # self.fail(msg='made it here')    # Uncomment to verify called

        # T2 should not finish successfully, should get a cancel error
        d2.addCallbacks(self.unexpected_success, expected_error)

        # Run it
        self.runner.start()
        return defer.gatherResults([d1, d2], consumeErrors=True)

    @deferred(timeout=3)
    def test_watchdog_timeout(self):
        t = SimpleTask(None, DEVICE_ID, delay=2)

        self.assertEqual(self.runner.pending_tasks, 0)
        self.assertEqual(self.runner.running_tasks, 0)
        self.assertEqual(self.runner.watchdog_timeouts, 0)

        # Actual watchdog minimum is probably to long for an automated test, reach
        # around and force ti to something smaller (kids, don't try this at home)

        t._watchdog_timeout = 0.1
        self.runner.queue_task(t)

        self.assertEqual(self.runner.pending_tasks, 1)
        self.assertEqual(self.runner.running_tasks, 0)

        def expected_error(failure):
            from voltha.extensions.omci.tasks.task import WatchdogTimeoutFailure
            self.assertTrue(isinstance(failure.value, WatchdogTimeoutFailure))
            self.assertEqual(self.runner.pending_tasks, 0)
            self.assertEqual(self.runner.running_tasks, 0)
            self.assertEqual(self.runner.successful_tasks_completed, 0)
            self.assertEqual(self.runner.failed_tasks, 1)
            self.assertEqual(self.runner.watchdog_timeouts, 1)
            self.assertEqual(self.runner.last_watchdog_failure_task, t.name)
            # self.fail(msg='made it here')    # Uncomment to verify called

        t.deferred.addCallbacks(self.unexpected_success, expected_error)

        # Run it
        self.runner.start()
        return t.deferred
Esempio n. 7
0
class TestTaskRunner(TestCase):
    """
    Test the Task Runner Object
    """
    def setUp(self):
        # defer.setDebugging(True)
        self.runner = TaskRunner(DEVICE_ID)

    def tearDown(self):
        r, self.runner = self.runner, None
        r.stop()

    def test_default_init(self):
        self.assertFalse(self.runner.active)
        self.assertEqual(self.runner.pending_tasks, 0)
        self.assertEqual(self.runner.running_tasks, 0)
        self.assertEqual(self.runner.successful_tasks_completed, 0)
        self.assertEqual(self.runner.failed_tasks, 0)

    def test_start_stop(self):
        self.assertFalse(self.runner.active)

        self.runner.start()
        self.assertTrue(self.runner.active)

        self.runner.stop()
        self.assertFalse(self.runner.active)

    def test_simple_task_init(self):
        t = SimpleTask(None,
                       DEVICE_ID,
                       exclusive=True,
                       priority=0,
                       success=True,
                       value=0,
                       delay=0)

        self.assertEqual(t.priority, 0)
        self.assertGreater(t.task_id, 0)
        self.assertTrue(t.exclusive)
        self.assertFalse(t.deferred.called)

    @raises(AssertionError)
    def test_simple_negative_priority(self):
        SimpleTask(None, DEVICE_ID, priority=-1)

    @raises(AssertionError)
    def test_simple_big_priority(self):
        SimpleTask(None, DEVICE_ID, priority=256)

    def unexpected_error(self, _failure):
        self.assertEqual('Should not be here, expected success', _failure)

    def unexpected_success(self, _results):
        self.assertEqual('Should not be here, expected a failure', _results)

    @deferred(timeout=5)
    def test_simple_success(self):
        expected_result = 123

        t = SimpleTask(None,
                       DEVICE_ID,
                       exclusive=True,
                       priority=0,
                       success=True,
                       value=expected_result,
                       delay=0)

        d = self.runner.queue_task(t)
        self.assertEqual(self.runner.pending_tasks, 1)
        self.assertEqual(self.runner.running_tasks, 0)
        self.runner.start()

        def check_results(results):
            self.assertEqual(results, expected_result)
            self.assertEqual(self.runner.pending_tasks, 0)
            self.assertEqual(self.runner.running_tasks, 0)
            self.assertEqual(self.runner.successful_tasks_completed, 1)
            self.assertEqual(self.runner.failed_tasks, 0)
            self.assertTrue(self.runner.active)
            return results

        d.addCallbacks(check_results, self.unexpected_error)
        return d

    @raises(Exception)
    @deferred(timeout=5)
    def test_simple_failure(self):
        self.expected_failure = Exception('Testing a task failure')

        t = SimpleTask(None,
                       DEVICE_ID,
                       exclusive=True,
                       priority=0,
                       success=False,
                       value=self.expected_failure,
                       delay=0)

        d = self.runner.queue_task(t)
        self.assertEqual(self.runner.pending_tasks, 1)
        self.assertEqual(self.runner.running_tasks, 0)
        self.runner.start()

        def expected_failure(failure):
            self.assertEqual(failure, self.expected_failure)
            self.assertEqual(self.runner.pending_tasks, 0)
            self.assertEqual(self.runner.running_tasks, 0)
            self.assertEqual(self.runner.successful_tasks_completed, 0)
            self.assertEqual(self.runner.failed_tasks, 1)
            self.assertTrue(self.runner.active)
            return failure

        d.addCallbacks(self.unexpected_success, expected_failure)
        return d

    @deferred(timeout=5)
    def test_priority(self):
        self.last_value_set = 0

        t1 = SimpleTask(None,
                        DEVICE_ID,
                        exclusive=True,
                        priority=1,
                        success=True,
                        value=1,
                        delay=0)

        t2 = SimpleTask(
            None,
            DEVICE_ID,
            exclusive=True,
            priority=2,  # Should finish first
            success=True,
            value=2,
            delay=0)

        d1 = self.runner.queue_task(t1)
        d2 = self.runner.queue_task(t2)

        def set_last_value(results):
            self.last_value_set = results

        d1.addCallbacks(set_last_value, self.unexpected_error)
        d2.addCallbacks(set_last_value, self.unexpected_error)

        self.assertEqual(self.runner.pending_tasks, 2)
        self.assertEqual(self.runner.running_tasks, 0)

        d = defer.gatherResults([d1, d2], consumeErrors=True)

        def check_results(_):
            self.assertEqual(self.last_value_set, 1)
            self.assertEqual(self.runner.pending_tasks, 0)
            self.assertEqual(self.runner.running_tasks, 0)
            self.assertEqual(self.runner.successful_tasks_completed, 2)

        d.addCallbacks(check_results, self.unexpected_error)

        self.runner.start()
        return d

    @inlineCallbacks
    def check_that_t1_t2_running_and_last_is_not(self, results):
        from common.utils.asleep import asleep
        yield asleep(0.1)

        self.assertEqual(self.runner.pending_tasks, 1)
        self.assertEqual(self.runner.running_tasks, 2)
        self.assertEqual(self.runner.successful_tasks_completed, 1)

        returnValue(results)

    @deferred(timeout=10)
    def test_concurrent(self):
        blocker = SimpleTask(None,
                             DEVICE_ID,
                             exclusive=True,
                             priority=10,
                             success=True,
                             value=1,
                             delay=0.5)

        t1 = SimpleTask(None,
                        DEVICE_ID,
                        exclusive=False,
                        priority=9,
                        success=True,
                        value=1,
                        delay=2)

        t2 = SimpleTask(None,
                        DEVICE_ID,
                        exclusive=False,
                        priority=9,
                        success=True,
                        value=1,
                        delay=2)

        last = SimpleTask(None,
                          DEVICE_ID,
                          exclusive=True,
                          priority=8,
                          success=True,
                          value=1,
                          delay=0)

        d0 = self.runner.queue_task(blocker)
        d0.addCallbacks(self.check_that_t1_t2_running_and_last_is_not,
                        self.unexpected_error)

        d1 = self.runner.queue_task(t1)
        d2 = self.runner.queue_task(t2)
        d3 = self.runner.queue_task(last)

        self.assertEqual(self.runner.pending_tasks, 4)
        self.assertEqual(self.runner.running_tasks, 0)

        d = defer.gatherResults([d0, d1, d2, d3], consumeErrors=True)

        def check_final_results(_):
            self.assertEqual(self.runner.pending_tasks, 0)
            self.assertEqual(self.runner.running_tasks, 0)
            self.assertEqual(self.runner.successful_tasks_completed, 4)
            self.assertEqual(self.runner.failed_tasks, 0)

        d.addCallbacks(check_final_results, self.unexpected_error)

        self.runner.start()
        return d

    @raises(CancelledError)
    @deferred(timeout=2)
    def test_cancel_queued(self):
        t = SimpleTask(None,
                       DEVICE_ID,
                       exclusive=True,
                       priority=9,
                       success=True,
                       value=1,
                       delay=0)

        d = self.runner.queue_task(t)
        self.assertEqual(self.runner.pending_tasks, 1)
        self.assertEqual(self.runner.running_tasks, 0)

        self.runner.cancel_task(t.task_id)
        self.assertEqual(self.runner.pending_tasks, 0)
        self.assertEqual(self.runner.running_tasks, 0)
        return d

    @deferred(timeout=200)
    def test_cancel_running(self):
        t1 = SimpleTask(None,
                        DEVICE_ID,
                        exclusive=False,
                        priority=9,
                        success=True,
                        value=1,
                        delay=0.5)
        t2 = SimpleTask(None,
                        DEVICE_ID,
                        exclusive=False,
                        priority=9,
                        success=True,
                        value=1,
                        delay=200)

        d1 = self.runner.queue_task(t1)
        d2 = self.runner.queue_task(t2)

        self.assertEqual(self.runner.pending_tasks, 2)
        self.assertEqual(self.runner.running_tasks, 0)

        def kill_task_t2(_, task_id):
            self.assertEqual(self.runner.pending_tasks, 0)
            self.assertEqual(self.runner.running_tasks, 1)

            self.runner.cancel_task(task_id)
            self.assertEqual(self.runner.running_tasks, 0)

        d1.addCallbacks(kill_task_t2,
                        self.unexpected_error,
                        callbackArgs=[t2.task_id])

        def expected_error(failure):
            self.assertTrue(isinstance(failure.value, CancelledError))
            self.assertEqual(self.runner.pending_tasks, 0)
            self.assertEqual(self.runner.running_tasks, 0)
            self.assertEqual(self.runner.successful_tasks_completed, 1)
            self.assertEqual(self.runner.failed_tasks, 1)

        d2.addCallbacks(self.unexpected_success, expected_error)

        self.runner.start()
        return defer.gatherResults([d1, d2], consumeErrors=True)
Esempio n. 8
0
    def __init__(self, omci_agent, device_id, adapter_agent, custom_me_map,
                 mib_db, alarm_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 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._runner = TaskRunner(device_id)  # OMCI_CC Task runner
        self._deferred = None
        self._first_in_sync = False
        self._first_capabilities = False

        # 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)
        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._alarm_sync_sm,
        ]
        self._on_sync_state_machines = [  # Run after first in_sync event
        ]
        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)
Esempio n. 9
0
class OnuDeviceEntry(object):
    """
    An ONU Device entry in the MIB
    """
    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)

    @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 task_runner(self):
        return self._runner

    @property
    def mib_synchronizer(self):
        return self.mib_sync

    @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

            # 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)

    def start(self):
        if self._started:
            return

        self._started = True
        self._omci_cc.enabled = True
        self._runner.start()

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

        def start_state_machines(machines):
            for sm in machines:
                sm.start()

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

    def stop(self):
        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()

        # Stop task runner
        self._runner.stop()

        # Notify any event listeners
        self._publish_device_status_event()

    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': self._started}
        self.event_bus.publish(topic=topic, msg=msg)

    def delete(self):
        self.stop()

    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
Esempio n. 10
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, 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

        # 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-syncronizer')
            advertise = alarm_synchronizer_info['advertise-events']
            self._alarm_sync_sm = alarm_synchronizer_info['state-machine'](self._omci_agent,
                                                                           device_id,
                                                                           alarm_synchronizer_info['tasks'],
                                                                           mib_db,
                                                                           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._alarm_sync_sm,
        ]
        self._on_sync_state_machines = [        # Run after first in_sync event
            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)

    @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 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

    @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,
                LAST_IN_SYNC_KEY: self.alarm_synchronizer.last_alarm_sync_time
            }
            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

    def start(self):
        """
        Start the ONU Device Entry state machines
        """
        if self._started:
            return

        self._started = True
        self._omci_cc.enabled = True
        self._first_in_sync = 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)

    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.
        """
        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 reboot(self,
               flags=RebootFlags.Reboot_Unconditionally,
               timeout=OmciRebootRequest.DEFAULT_PRIORITY):
        """
        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))