Example #1
0
class AdtranOnuHandler(AdtranXPON):
    def __init__(self, adapter, device_id):
        kwargs = dict()
        super(AdtranOnuHandler, self).__init__(**kwargs)
        self.adapter = adapter
        self.adapter_agent = adapter.adapter_agent
        self.device_id = device_id
        self.log = structlog.get_logger(device_id=device_id)
        self.logical_device_id = None
        self.proxy_address = None
        self._event_messages = None
        self._enabled = False
        self.pm_metrics = None
        self.alarms = None
        self._mgmt_gemport_aes = False
        self._upstream_channel_speed = 0

        self._unis = dict()  # Port # -> UniPort
        self._pons = dict()  # Port # -> PonPort
        self._heartbeat = HeartBeat.create(self, device_id)

        self._deferred = None
        self._event_deferred = None
        self._omci = None
        self._port_number_pool = IndexPool(_MAXIMUM_PORT, 1)

        self._olt_created = False  # True if deprecated method of OLT creating DA is used
        self._is_mock = False

    def __str__(self):
        return "AdtranOnuHandler: {}".format(self.device_id)

    def _cancel_deferred(self):
        d1, self._deferred = self._deferred, None
        d2, self._event_deferred = self._event_deferred, None

        for d in [d1, d2]:
            try:
                if d is not None and not d.called:
                    d.cancel()
            except:
                pass

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

    @enabled.setter
    def enabled(self, value):
        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 mgmt_gemport_aes(self):
        return self._mgmt_gemport_aes

    @mgmt_gemport_aes.setter
    def mgmt_gemport_aes(self, value):
        if self._mgmt_gemport_aes != value:
            self._mgmt_gemport_aes = value
            # TODO: Anything else

    @property
    def upstream_channel_speed(self):
        return self._upstream_channel_speed

    @upstream_channel_speed.setter
    def upstream_channel_speed(self, value):
        if self._upstream_channel_speed != value:
            self._upstream_channel_speed = value
            # TODO: Anything else

    @property
    def is_mock(self):
        return self._is_mock  # Not pointing to real hardware

    @property
    def olt_created(self):
        return self._olt_created  # ONU was created with deprecated 'child_device_detected' call

    @property
    def omci(self):
        return self._omci

    @property
    def heartbeat(self):
        return self._heartbeat

    @property
    def uni_ports(self):
        return self._unis.values()

    def uni_port(self, port_no_or_name):
        if isinstance(port_no_or_name, (str, unicode)):
            return next(
                (uni for uni in self.uni_ports if uni.name == port_no_or_name),
                None)

        assert isinstance(port_no_or_name, int), 'Invalid parameter type'
        return self._unis.get(port_no_or_name)

    @property
    def pon_ports(self):
        return self._pons.values()

    def pon_port(self, port_no):
        return self._pons.get(port_no)

    @property
    def _next_port_number(self):
        return self._port_number_pool.get_next()

    def _release_port_number(self, number):
        self._port_number_pool.release(number)

    def start(self):
        assert self._enabled, 'Start should only be called if enabled'
        #
        # TODO: Perform common startup tasks here
        #
        self._cancel_deferred()

        self._omci = OMCI_CC(self.adapter_agent,
                             self.device_id,
                             custom_me_entries=onu_custom_entity_classes)
        self._omci.enabled = True

        # Handle received ONU event messages
        self._event_messages = DeferredQueue()
        self._event_deferred = reactor.callLater(0, self._handle_onu_events)

        # Register for adapter messages
        self.adapter_agent.register_for_inter_adapter_messages()

        # Port startup
        for port in self.uni_ports:
            port.enabled = True

        for port in self.pon_ports:
            port.enabled = True

        # Heartbeat
        self._heartbeat.enabled = True

    def stop(self):
        assert not self._enabled, 'Stop should only be called if disabled'
        #
        # TODO: Perform common shutdown tasks here
        #
        self._cancel_deferred()

        # Drop registration for adapter messages
        self.adapter_agent.unregister_for_inter_adapter_messages()

        # Heartbeat
        self._heartbeat.stop()

        # Port shutdown
        for port in self.uni_ports:
            port.enabled = False

        for port in self.pon_ports:
            port.enabled = False

        omci, self._omci = self._omci, None
        if omci is not None:
            omci.enabled = False

        queue, self._event_deferred = self._event_deferred, None
        if queue is not None:
            while queue.pending:
                _ = yield queue.get()

    def receive_message(self, msg):
        if self._omci is not None and self.enabled:
            self._omci.receive_message(msg)

    def activate(self, device):
        self.log.info('activating')

        # first we verify that we got parent reference and proxy info
        assert device.parent_id, 'Invalid Parent ID'
        assert device.proxy_address.device_id, 'Invalid Device ID'

        if device.vlan:
            # vlan non-zero if created via legacy method (not xPON). Also
            # Set a random serial number since not xPON based
            self._olt_created = True

        # register for proxied messages right away
        self.proxy_address = device.proxy_address
        self.adapter_agent.register_for_proxied_messages(device.proxy_address)

        # initialize device info
        device.root = True
        device.vendor = 'Adtran Inc.'
        device.model = 'n/a'
        device.hardware_version = 'n/a'
        device.firmware_version = 'n/a'
        device.reason = ''

        # TODO: Support more versions as needed
        images = Image(version='NOT AVAILABLE')
        device.images.image.extend([images])

        device.connect_status = ConnectStatus.UNKNOWN

        ############################################################################
        # Setup PM configuration for this device

        self.pm_metrics = OnuPmMetrics(self,
                                       device,
                                       grouped=True,
                                       freq_override=False)
        pm_config = self.pm_metrics.make_proto()
        self.log.info("initial-pm-config", pm_config=pm_config)
        self.adapter_agent.update_device_pm_config(pm_config, init=True)

        ############################################################################
        # Setup Alarm handler

        self.alarms = AdapterAlarms(self.adapter, device.id)

        # reference of uni_port is required when re-enabling the device if
        # it was disabled previously
        # Need to query ONU for number of supported uni ports
        # For now, temporarily set number of ports to 1 - port #2

        # Register physical ports.  Should have at least one of each

        pon_port = PonPort.create(self, self._next_port_number)

        self._pons[pon_port.port_number] = pon_port
        self.adapter_agent.add_port(device.id, pon_port.get_port())

        parent_device = self.adapter_agent.get_device(device.parent_id)
        self.logical_device_id = parent_device.parent_id
        assert self.logical_device_id, 'Invalid logical device ID'

        if self._olt_created:
            # vlan non-zero if created via legacy method (not xPON). Also
            # Set a random serial number since not xPON based

            uni_port = UniPort.create(self, self._next_port_number,
                                      'deprecated', device.vlan)
            self._unis[uni_port.port_number] = uni_port
            self.adapter_agent.add_port(device.id, uni_port.get_port())

            device.serial_number = uuid4().hex
            uni_port.add_logical_port(device.vlan, control_vlan=device.vlan)

            # Start things up for this ONU Handler.
            self.enabled = True

        # Start collecting stats from the device after a brief pause
        reactor.callLater(30, self.start_kpi_collection, device.id)

        self.adapter_agent.update_device(device)

    def reconcile(self, device):
        self.log.info('reconciling-ONU-device-starts')

        # first we verify that we got parent reference and proxy info
        assert device.parent_id
        assert device.proxy_address.device_id
        # assert device.proxy_address.channel_id
        self._cancel_deferred()

        # register for proxied messages right away
        self.proxy_address = device.proxy_address
        self.adapter_agent.register_for_proxied_messages(device.proxy_address)

        # Register for adapter messages
        self.adapter_agent.register_for_inter_adapter_messages()

        # Set the connection status to REACHABLE
        device.connect_status = ConnectStatus.REACHABLE
        self.adapter_agent.update_device(device)
        self.enabled = True

        # TODO: Verify that the uni, pon and logical ports exists

        # Mark the device as REACHABLE and ACTIVE
        device = self.adapter_agent.get_device(device.id)
        device.connect_status = ConnectStatus.REACHABLE
        device.oper_status = OperStatus.ACTIVE
        device.reason = ''
        self.adapter_agent.update_device(device)

        self.log.info('reconciling-ONU-device-ends')

    def update_pm_config(self, device, pm_config):
        # TODO: This has not been tested
        self.log.info('update_pm_config', pm_config=pm_config)
        self.pm_metrics.update(pm_config)

    def start_kpi_collection(self, device_id):
        # TODO: This has not been tested
        def _collect(device_id, prefix):
            from voltha.protos.events_pb2 import KpiEvent, KpiEventType, MetricValuePairs

            if self.enabled:
                try:
                    # Step 1: gather metrics from device
                    port_metrics = self.pm_metrics.collect_port_metrics()

                    # Step 2: prepare the KpiEvent for submission
                    # we can time-stamp them here or could use time derived from OLT
                    ts = arrow.utcnow().timestamp
                    kpi_event = KpiEvent(
                        type=KpiEventType.slice,
                        ts=ts,
                        prefixes={
                            prefix + '.{}'.format(k):
                            MetricValuePairs(metrics=port_metrics[k])
                            for k in port_metrics.keys()
                        })
                    # Step 3: submit
                    self.adapter_agent.submit_kpis(kpi_event)

                except Exception as e:
                    self.log.exception('failed-to-submit-kpis', e=e)

        self.pm_metrics.start_collector(_collect)

    @inlineCallbacks
    def update_flow_table(self, device, flows):
        #
        # We need to proxy through the OLT to get to the ONU
        # Configuration from here should be using OMCI
        #
        # self.log.info('bulk-flow-update', device_id=device.id, flows=flows)

        import voltha.core.flow_decomposer as fd
        from voltha.protos.openflow_13_pb2 import OFPXMC_OPENFLOW_BASIC, ofp_port

        def is_downstream(port):
            return port == 100  # Need a better way

        def is_upstream(port):
            return not is_downstream(port)

        omci = self._omci

        for flow in flows:
            _type = None
            _port = None
            _vlan_vid = None
            _udp_dst = None
            _udp_src = None
            _ipv4_dst = None
            _ipv4_src = None
            _metadata = None
            _output = None
            _push_tpid = None
            _field = None
            _set_vlan_vid = None
            self.log.info('bulk-flow-update', device_id=device.id, flow=flow)
            try:
                _in_port = fd.get_in_port(flow)
                assert _in_port is not None

                if is_downstream(_in_port):
                    self.log.info('downstream-flow')
                elif is_upstream(_in_port):
                    self.log.info('upstream-flow')
                else:
                    raise Exception('port should be 1 or 2 by our convention')

                _out_port = fd.get_out_port(flow)  # may be None
                self.log.info('out-port', out_port=_out_port)

                for field in fd.get_ofb_fields(flow):
                    if field.type == fd.ETH_TYPE:
                        _type = field.eth_type
                        self.log.info('field-type-eth-type', eth_type=_type)

                    elif field.type == fd.IP_PROTO:
                        _proto = field.ip_proto
                        self.log.info('field-type-ip-proto', ip_proto=_proto)

                    elif field.type == fd.IN_PORT:
                        _port = field.port
                        self.log.info('field-type-in-port', in_port=_port)

                    elif field.type == fd.VLAN_VID:
                        _vlan_vid = field.vlan_vid & 0xfff
                        self.log.info('field-type-vlan-vid', vlan=_vlan_vid)

                    elif field.type == fd.VLAN_PCP:
                        _vlan_pcp = field.vlan_pcp
                        self.log.info('field-type-vlan-pcp', pcp=_vlan_pcp)

                    elif field.type == fd.UDP_DST:
                        _udp_dst = field.udp_dst
                        self.log.info('field-type-udp-dst', udp_dst=_udp_dst)

                    elif field.type == fd.UDP_SRC:
                        _udp_src = field.udp_src
                        self.log.info('field-type-udp-src', udp_src=_udp_src)

                    elif field.type == fd.IPV4_DST:
                        _ipv4_dst = field.ipv4_dst
                        self.log.info('field-type-ipv4-dst',
                                      ipv4_dst=_ipv4_dst)

                    elif field.type == fd.IPV4_SRC:
                        _ipv4_src = field.ipv4_src
                        self.log.info('field-type-ipv4-src',
                                      ipv4_dst=_ipv4_src)

                    elif field.type == fd.METADATA:
                        _metadata = field.table_metadata
                        self.log.info('field-type-metadata',
                                      metadata=_metadata)

                    else:
                        raise NotImplementedError('field.type={}'.format(
                            field.type))

                for action in fd.get_actions(flow):

                    if action.type == fd.OUTPUT:
                        _output = action.output.port
                        self.log.info('action-type-output',
                                      output=_output,
                                      in_port=_in_port)

                    elif action.type == fd.POP_VLAN:
                        self.log.info('action-type-pop-vlan', in_port=_in_port)

                    elif action.type == fd.PUSH_VLAN:
                        _push_tpid = action.push.ethertype
                        log.info('action-type-push-vlan',
                                 push_tpid=_push_tpid,
                                 in_port=_in_port)
                        if action.push.ethertype != 0x8100:
                            self.log.error('unhandled-tpid',
                                           ethertype=action.push.ethertype)

                    elif action.type == fd.SET_FIELD:
                        _field = action.set_field.field.ofb_field
                        assert (action.set_field.field.oxm_class ==
                                OFPXMC_OPENFLOW_BASIC)
                        self.log.info('action-type-set-field',
                                      field=_field,
                                      in_port=_in_port)
                        if _field.type == fd.VLAN_VID:
                            _set_vlan_vid = _field.vlan_vid & 0xfff
                            self.log.info('set-field-type-valn-vid',
                                          _set_vlan_vid)
                        else:
                            self.log.error('unsupported-action-set-field-type',
                                           field_type=_field.type)
                    else:
                        log.error('unsupported-action-type',
                                  action_type=action.type,
                                  in_port=_in_port)

                #
                # All flows created from ONU adapter should be OMCI based
                #
                if _vlan_vid == 0 and _set_vlan_vid != None and _set_vlan_vid != 0:
                    # allow priority tagged packets
                    # Set AR - ExtendedVlanTaggingOperationConfigData
                    #          514 - RxVlanTaggingOperationTable - add VLAN <cvid> to priority tagged pkts - c-vid

                    results = yield omci.send_delete_vlan_tagging_filter_data(
                        0x2102)

                    # self.send_set_vlan_tagging_filter_data(0x2102, _set_vlan_vid)
                    results = yield omci.send_create_vlan_tagging_filter_data(
                        0x2102, _set_vlan_vid)

                    results = yield omci.send_set_extended_vlan_tagging_operation_vlan_configuration_data_untagged(
                        0x202, 0x1000, _set_vlan_vid)

                    results = yield omci.send_set_extended_vlan_tagging_operation_vlan_configuration_data_single_tag(
                        0x202, 8, 0, 0, 1, 8, _set_vlan_vid)

                    # Set AR - ExtendedVlanTaggingOperationConfigData
                    #          514 - RxVlanTaggingOperationTable - add VLAN <cvid> to priority tagged pkts - c-vid
                    '''
                    results = yield omci.send_set_extended_vlan_tagging_operation_vlan_configuration_data_single_tag(0x205, 8, 0, 0,
                                                   
                    '''

            except Exception as e:
                log.exception('failed-to-install-flow', e=e, flow=flow)

    @inlineCallbacks
    def reboot(self):
        from common.utils.asleep import asleep
        self.log.info('rebooting', device_id=self.device_id)
        self._cancel_deferred()

        # Drop registration for adapter messages
        self.adapter_agent.unregister_for_inter_adapter_messages()

        # Update the operational status to ACTIVATING and connect status to
        # UNREACHABLE
        device = self.adapter_agent.get_device(self.device_id)
        previous_oper_status = device.oper_status
        previous_conn_status = device.connect_status
        device.oper_status = OperStatus.ACTIVATING
        device.connect_status = ConnectStatus.UNREACHABLE
        device.reason = 'Rebooting'

        self.adapter_agent.update_device(device)

        # Sleep 10 secs, simulating a reboot
        # TODO: send alert and clear alert after the reboot
        yield asleep(10)  # TODO: Need to reboot for real

        # Register for adapter messages
        self.adapter_agent.register_for_inter_adapter_messages()

        # Change the operational status back to its previous state.  With a
        # real OLT the operational state should be the state the device is
        # after a reboot.
        # Get the latest device reference
        device = self.adapter_agent.get_device(self.device_id)
        device.oper_status = previous_oper_status
        device.connect_status = previous_conn_status
        device.reason = ''
        self.adapter_agent.update_device(device)
        self.log.info('rebooted', device_id=self.device_id)

    def self_test_device(self, device):
        """
        This is called to Self a device based on a NBI call.
        :param device: A Voltha.Device object.
        :return: Will return result of self test
        """
        from voltha.protos.voltha_pb2 import SelfTestResponse
        self.log.info('self-test-device', device=device.id)
        # TODO: Support self test?
        return SelfTestResponse(result=SelfTestResponse.NOT_SUPPORTED)

    def disable(self):
        self.log.info('disabling', device_id=self.device_id)
        self.enabled = False

        # Get the latest device reference
        device = self.adapter_agent.get_device(self.device_id)

        # Disable all ports on that device
        self.adapter_agent.disable_all_ports(self.device_id)

        # Update the device operational status to UNKNOWN
        device.oper_status = OperStatus.UNKNOWN
        device.connect_status = ConnectStatus.UNREACHABLE
        device.reason = 'Disabled'
        self.adapter_agent.update_device(device)

        # Remove the uni logical port from the OLT, if still present
        parent_device = self.adapter_agent.get_device(device.parent_id)
        assert parent_device
        logical_device_id = parent_device.parent_id
        assert logical_device_id

        for uni in self.uni_ports:
            port_id = 'uni-{}'.format(uni.port_number)

            try:
                port = self.adapter_agent.get_logical_port(
                    logical_device_id, port_id)
                self.adapter_agent.delete_logical_port(logical_device_id, port)
            except KeyError:
                self.log.info('logical-port-not-found',
                              device_id=self.device_id,
                              portid=port_id)

        # Remove pon port from parent
        for port in self.pon_ports:
            self.adapter_agent.delete_port_reference_from_parent(
                self.device_id, port.get_port())

        # Just updating the port status may be an option as well
        # port.ofp_port.config = OFPPC_NO_RECV
        # yield self.adapter_agent.update_logical_port(logical_device_id,
        #                                             port)
        # Unregister for proxied message
        self.adapter_agent.unregister_for_proxied_messages(
            device.proxy_address)

        # TODO:
        # 1) Remove all flows from the device
        # 2) Remove the device from ponsim

        self.log.info('disabled', device_id=device.id)

    def reenable(self):
        self.log.info('re-enabling', device_id=self.device_id)
        try:
            # Get the latest device reference
            device = self.adapter_agent.get_device(self.device_id)
            self._cancel_deferred()

            # First we verify that we got parent reference and proxy info
            assert device.parent_id
            assert device.proxy_address.device_id
            # assert device.proxy_address.channel_id

            # Re-register for proxied messages right away
            self.proxy_address = device.proxy_address
            self.adapter_agent.register_for_proxied_messages(
                device.proxy_address)

            # Re-enable the ports on that device
            self.adapter_agent.enable_all_ports(self.device_id)

            # Refresh the port reference
            # self.uni_port = self._get_uni_port()   deprecated

            # Add the pon port reference to the parent
            for port in self.pon_ports:
                # TODO: Send 'enable' to PonPort?
                self.adapter_agent.add_port_reference_to_parent(
                    device.id, port.get_port())

            # Update the connect status to REACHABLE
            device.connect_status = ConnectStatus.REACHABLE
            self.adapter_agent.update_device(device)

            # re-add uni port to logical device
            parent_device = self.adapter_agent.get_device(device.parent_id)
            self.logical_device_id = parent_device.parent_id
            assert self.logical_device_id, 'Invalid logical device ID'

            if self.olt_created:
                # vlan non-zero if created via legacy method (not xPON)
                self.uni_port('deprecated').add_logical_port(
                    device.vlan, device.vlan, control_vlan=device.vlan)

            device = self.adapter_agent.get_device(device.id)
            device.oper_status = OperStatus.ACTIVE
            device.reason = ''

            self.enabled = True
            self.adapter_agent.update_device(device)

            self.log.info('re-enabled', device_id=device.id)
        except Exception, e:
            self.log.exception('error-reenabling', e=e)
class AdtranOnuHandler(AdtranXPON):
    def __init__(self, adapter, device_id):
        kwargs = dict()
        super(AdtranOnuHandler, self).__init__(**kwargs)
        self.adapter = adapter
        self.adapter_agent = adapter.adapter_agent
        self.device_id = device_id
        self.log = structlog.get_logger(device_id=device_id)
        self.logical_device_id = None
        self.proxy_address = None
        self._event_messages = None
        self._enabled = False
        self.pm_metrics = None
        self.alarms = None
        self._mgmt_gemport_aes = False
        self._upstream_channel_speed = 0

        self._unis = dict()         # Port # -> UniPort
        self._pons = dict()         # Port # -> PonPort
        self._heartbeat = HeartBeat.create(self, device_id)

        self._deferred = None
        self._event_deferred = None
        self._omci = None
        self._port_number_pool = IndexPool(_MAXIMUM_PORT, 1)

        self._olt_created = False   # True if deprecated method of OLT creating DA is used
        self._is_mock = False

    def __str__(self):
        return "AdtranOnuHandler: {}".format(self.device_id)

    def _cancel_deferred(self):
        d1, self._deferred = self._deferred, None
        d2, self._event_deferred = self._event_deferred, None

        for d in [d1, d2]:
            try:
                if d is not None and not d.called:
                    d.cancel()
            except:
                pass

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

    @enabled.setter
    def enabled(self, value):
        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 mgmt_gemport_aes(self):
        return self._mgmt_gemport_aes

    @mgmt_gemport_aes.setter
    def mgmt_gemport_aes(self, value):
        if self._mgmt_gemport_aes != value:
            self._mgmt_gemport_aes = value
            # TODO: Anything else

    @property
    def upstream_channel_speed(self):
        return self._upstream_channel_speed

    @upstream_channel_speed.setter
    def upstream_channel_speed(self, value):
        if self._upstream_channel_speed != value:
            self._upstream_channel_speed = value
            # TODO: Anything else

    @property
    def is_mock(self):
        return self._is_mock        # Not pointing to real hardware

    @property
    def olt_created(self):
        return self._olt_created    # ONU was created with deprecated 'child_device_detected' call

    @property
    def omci(self):
        return self._omci

    @property
    def heartbeat(self):
        return self._heartbeat

    @property
    def uni_ports(self):
        return self._unis.values()

    def uni_port(self, port_no_or_name):
        if isinstance(port_no_or_name, (str, unicode)):
            return next((uni for uni in self.uni_ports
                         if uni.name == port_no_or_name), None)

        assert isinstance(port_no_or_name, int), 'Invalid parameter type'
        return self._unis.get(port_no_or_name)

    @property
    def pon_ports(self):
        return self._pons.values()

    def pon_port(self, port_no):
        return self._pons.get(port_no)

    @property
    def _next_port_number(self):
        return self._port_number_pool.get_next()

    def _release_port_number(self, number):
        self._port_number_pool.release(number)

    def start(self):
        assert self._enabled, 'Start should only be called if enabled'
        #
        # TODO: Perform common startup tasks here
        #
        self._cancel_deferred()

        self._omci = OMCI_CC(self.adapter_agent,
                             self.device_id,
                             custom_me_entries=onu_custom_entity_classes)
        self._omci.enabled = True

        # Handle received ONU event messages
        self._event_messages = DeferredQueue()
        self._event_deferred = reactor.callLater(0, self._handle_onu_events)

        # Register for adapter messages
        self.adapter_agent.register_for_inter_adapter_messages()

        # Port startup
        for port in self.uni_ports:
            port.enabled = True

        for port in self.pon_ports:
            port.enabled = True

        # Heartbeat
        self._heartbeat.enabled = True

    def stop(self):
        assert not self._enabled, 'Stop should only be called if disabled'
        #
        # TODO: Perform common shutdown tasks here
        #
        self._cancel_deferred()

        # Drop registration for adapter messages
        self.adapter_agent.unregister_for_inter_adapter_messages()

        # Heartbeat
        self._heartbeat.stop()

        # Port shutdown
        for port in self.uni_ports:
            port.enabled = False

        for port in self.pon_ports:
            port.enabled = False

        omci, self._omci = self._omci, None
        if omci is not None:
            omci.enabled = False

        queue, self._event_deferred = self._event_deferred, None
        if queue is not None:
            while queue.pending:
                _ = yield queue.get()

    def receive_message(self, msg):
        if self._omci is not None and self.enabled:
            self._omci.receive_message(msg)

    def activate(self, device):
        self.log.info('activating')

        # first we verify that we got parent reference and proxy info
        assert device.parent_id, 'Invalid Parent ID'
        assert device.proxy_address.device_id, 'Invalid Device ID'

        if device.vlan:
            # vlan non-zero if created via legacy method (not xPON). Also
            # Set a random serial number since not xPON based
            self._olt_created = True

        # register for proxied messages right away
        self.proxy_address = device.proxy_address
        self.adapter_agent.register_for_proxied_messages(device.proxy_address)

        # initialize device info
        device.root = True
        device.vendor = 'Adtran Inc.'
        device.model = 'n/a'
        device.hardware_version = 'n/a'
        device.firmware_version = 'n/a'
        device.reason = ''

        # TODO: Support more versions as needed
        images = Image(version='NOT AVAILABLE')
        device.images.image.extend([images])

        device.connect_status = ConnectStatus.UNKNOWN

        ############################################################################
        # Setup PM configuration for this device

        self.pm_metrics = OnuPmMetrics(self, device, grouped=True, freq_override=False)
        pm_config = self.pm_metrics.make_proto()
        self.log.info("initial-pm-config", pm_config=pm_config)
        self.adapter_agent.update_device_pm_config(pm_config, init=True)

        ############################################################################
        # Setup Alarm handler

        self.alarms = AdapterAlarms(self.adapter, device.id)

        # reference of uni_port is required when re-enabling the device if
        # it was disabled previously
        # Need to query ONU for number of supported uni ports
        # For now, temporarily set number of ports to 1 - port #2

        # Register physical ports.  Should have at least one of each

        pon_port = PonPort.create(self, self._next_port_number)

        self._pons[pon_port.port_number] = pon_port
        self.adapter_agent.add_port(device.id, pon_port.get_port())

        parent_device = self.adapter_agent.get_device(device.parent_id)
        self.logical_device_id = parent_device.parent_id
        assert self.logical_device_id, 'Invalid logical device ID'

        if self._olt_created:
            # vlan non-zero if created via legacy method (not xPON). Also
            # Set a random serial number since not xPON based

            uni_port = UniPort.create(self, self._next_port_number,
                                      'deprecated', device.vlan)
            self._unis[uni_port.port_number] = uni_port
            self.adapter_agent.add_port(device.id, uni_port.get_port())

            device.serial_number = uuid4().hex
            uni_port.add_logical_port(device.vlan, control_vlan=device.vlan)

            # Start things up for this ONU Handler.
            self.enabled = True

        # Start collecting stats from the device after a brief pause
        reactor.callLater(30, self.start_kpi_collection, device.id)

        self.adapter_agent.update_device(device)

    def reconcile(self, device):
        self.log.info('reconciling-ONU-device-starts')

        # first we verify that we got parent reference and proxy info
        assert device.parent_id
        assert device.proxy_address.device_id
        # assert device.proxy_address.channel_id
        self._cancel_deferred()

        # register for proxied messages right away
        self.proxy_address = device.proxy_address
        self.adapter_agent.register_for_proxied_messages(device.proxy_address)

        # Register for adapter messages
        self.adapter_agent.register_for_inter_adapter_messages()

        # Set the connection status to REACHABLE
        device.connect_status = ConnectStatus.REACHABLE
        self.adapter_agent.update_device(device)
        self.enabled = True

        # TODO: Verify that the uni, pon and logical ports exists

        # Mark the device as REACHABLE and ACTIVE
        device = self.adapter_agent.get_device(device.id)
        device.connect_status = ConnectStatus.REACHABLE
        device.oper_status = OperStatus.ACTIVE
        device.reason = ''
        self.adapter_agent.update_device(device)

        self.log.info('reconciling-ONU-device-ends')

    def update_pm_config(self, device, pm_config):
        # TODO: This has not been tested
        self.log.info('update_pm_config', pm_config=pm_config)
        self.pm_metrics.update(pm_config)

    def start_kpi_collection(self, device_id):
        # TODO: This has not been tested
        def _collect(device_id, prefix):
            from voltha.protos.events_pb2 import KpiEvent, KpiEventType, MetricValuePairs

            if self.enabled:
                try:
                    # Step 1: gather metrics from device
                    port_metrics = self.pm_metrics.collect_port_metrics()

                    # Step 2: prepare the KpiEvent for submission
                    # we can time-stamp them here or could use time derived from OLT
                    ts = arrow.utcnow().timestamp
                    kpi_event = KpiEvent(
                        type=KpiEventType.slice,
                        ts=ts,
                        prefixes={
                            prefix + '.{}'.format(k): MetricValuePairs(metrics=port_metrics[k])
                            for k in port_metrics.keys()}
                    )
                    # Step 3: submit
                    self.adapter_agent.submit_kpis(kpi_event)

                except Exception as e:
                    self.log.exception('failed-to-submit-kpis', e=e)

        self.pm_metrics.start_collector(_collect)

    @inlineCallbacks
    def update_flow_table(self, device, flows):
        #
        # We need to proxy through the OLT to get to the ONU
        # Configuration from here should be using OMCI
        #
        # self.log.info('bulk-flow-update', device_id=device.id, flows=flows)

        import voltha.core.flow_decomposer as fd
        from voltha.protos.openflow_13_pb2 import OFPXMC_OPENFLOW_BASIC, ofp_port

        def is_downstream(port):
            return port == 100  # Need a better way

        def is_upstream(port):
            return not is_downstream(port)

        omci = self._omci

        for flow in flows:
            _type = None
            _port = None
            _vlan_vid = None
            _udp_dst = None
            _udp_src = None
            _ipv4_dst = None
            _ipv4_src = None
            _metadata = None
            _output = None
            _push_tpid = None
            _field = None
            _set_vlan_vid = None
            self.log.info('bulk-flow-update', device_id=device.id, flow=flow)
            try:
                _in_port = fd.get_in_port(flow)
                assert _in_port is not None

                if is_downstream(_in_port):
                    self.log.info('downstream-flow')
                elif is_upstream(_in_port):
                    self.log.info('upstream-flow')
                else:
                    raise Exception('port should be 1 or 2 by our convention')

                _out_port = fd.get_out_port(flow)  # may be None
                self.log.info('out-port', out_port=_out_port)

                for field in fd.get_ofb_fields(flow):
                    if field.type == fd.ETH_TYPE:
                        _type = field.eth_type
                        self.log.info('field-type-eth-type',
                                      eth_type=_type)

                    elif field.type == fd.IP_PROTO:
                        _proto = field.ip_proto
                        self.log.info('field-type-ip-proto',
                                      ip_proto=_proto)

                    elif field.type == fd.IN_PORT:
                        _port = field.port
                        self.log.info('field-type-in-port',
                                      in_port=_port)

                    elif field.type == fd.VLAN_VID:
                        _vlan_vid = field.vlan_vid & 0xfff
                        self.log.info('field-type-vlan-vid',
                                      vlan=_vlan_vid)

                    elif field.type == fd.VLAN_PCP:
                        _vlan_pcp = field.vlan_pcp
                        self.log.info('field-type-vlan-pcp',
                                      pcp=_vlan_pcp)

                    elif field.type == fd.UDP_DST:
                        _udp_dst = field.udp_dst
                        self.log.info('field-type-udp-dst',
                                      udp_dst=_udp_dst)

                    elif field.type == fd.UDP_SRC:
                        _udp_src = field.udp_src
                        self.log.info('field-type-udp-src',
                                      udp_src=_udp_src)

                    elif field.type == fd.IPV4_DST:
                        _ipv4_dst = field.ipv4_dst
                        self.log.info('field-type-ipv4-dst',
                                      ipv4_dst=_ipv4_dst)

                    elif field.type == fd.IPV4_SRC:
                        _ipv4_src = field.ipv4_src
                        self.log.info('field-type-ipv4-src',
                                      ipv4_dst=_ipv4_src)

                    elif field.type == fd.METADATA:
                        _metadata = field.table_metadata
                        self.log.info('field-type-metadata',
                                      metadata=_metadata)

                    else:
                        raise NotImplementedError('field.type={}'.format(
                            field.type))

                for action in fd.get_actions(flow):

                    if action.type == fd.OUTPUT:
                        _output = action.output.port
                        self.log.info('action-type-output',
                                      output=_output, in_port=_in_port)

                    elif action.type == fd.POP_VLAN:
                        self.log.info('action-type-pop-vlan',
                                      in_port=_in_port)

                    elif action.type == fd.PUSH_VLAN:
                        _push_tpid = action.push.ethertype
                        log.info('action-type-push-vlan',
                                 push_tpid=_push_tpid, in_port=_in_port)
                        if action.push.ethertype != 0x8100:
                            self.log.error('unhandled-tpid',
                                           ethertype=action.push.ethertype)

                    elif action.type == fd.SET_FIELD:
                        _field = action.set_field.field.ofb_field
                        assert (action.set_field.field.oxm_class ==
                                OFPXMC_OPENFLOW_BASIC)
                        self.log.info('action-type-set-field',
                                      field=_field, in_port=_in_port)
                        if _field.type == fd.VLAN_VID:
                            _set_vlan_vid = _field.vlan_vid & 0xfff
                            self.log.info('set-field-type-valn-vid', _set_vlan_vid)
                        else:
                            self.log.error('unsupported-action-set-field-type',
                                           field_type=_field.type)
                    else:
                        log.error('unsupported-action-type',
                                  action_type=action.type, in_port=_in_port)

                #
                # All flows created from ONU adapter should be OMCI based
                #
                if _vlan_vid == 0 and _set_vlan_vid != None and _set_vlan_vid != 0:
                    # allow priority tagged packets
                    # Set AR - ExtendedVlanTaggingOperationConfigData
                    #          514 - RxVlanTaggingOperationTable - add VLAN <cvid> to priority tagged pkts - c-vid

                    results = yield omci.send_delete_vlan_tagging_filter_data(0x2102)

                    # self.send_set_vlan_tagging_filter_data(0x2102, _set_vlan_vid)
                    results = yield omci.send_create_vlan_tagging_filter_data(
                                        0x2102,
                                        _set_vlan_vid)

                    results = yield omci.send_set_extended_vlan_tagging_operation_vlan_configuration_data_untagged(
                                        0x202,
                                        0x1000,
                                        _set_vlan_vid)

                    results = yield omci.send_set_extended_vlan_tagging_operation_vlan_configuration_data_single_tag(
                                        0x202,
                                        8,
                                        0,
                                        0,
                                        1,
                                        8,
                                        _set_vlan_vid)

                    # Set AR - ExtendedVlanTaggingOperationConfigData
                    #          514 - RxVlanTaggingOperationTable - add VLAN <cvid> to priority tagged pkts - c-vid
                    '''
                    results = yield omci.send_set_extended_vlan_tagging_operation_vlan_configuration_data_single_tag(0x205, 8, 0, 0,
                                                   
                    '''

            except Exception as e:
                log.exception('failed-to-install-flow', e=e, flow=flow)

    @inlineCallbacks
    def reboot(self):
        from common.utils.asleep import asleep
        self.log.info('rebooting', device_id=self.device_id)
        self._cancel_deferred()

        # Drop registration for adapter messages
        self.adapter_agent.unregister_for_inter_adapter_messages()

        # Update the operational status to ACTIVATING and connect status to
        # UNREACHABLE
        device = self.adapter_agent.get_device(self.device_id)
        previous_oper_status = device.oper_status
        previous_conn_status = device.connect_status
        device.oper_status = OperStatus.ACTIVATING
        device.connect_status = ConnectStatus.UNREACHABLE
        device.reason = 'Rebooting'

        self.adapter_agent.update_device(device)

        # Sleep 10 secs, simulating a reboot
        # TODO: send alert and clear alert after the reboot
        yield asleep(10)    # TODO: Need to reboot for real

        # Register for adapter messages
        self.adapter_agent.register_for_inter_adapter_messages()

        # Change the operational status back to its previous state.  With a
        # real OLT the operational state should be the state the device is
        # after a reboot.
        # Get the latest device reference
        device = self.adapter_agent.get_device(self.device_id)
        device.oper_status = previous_oper_status
        device.connect_status = previous_conn_status
        device.reason = ''
        self.adapter_agent.update_device(device)
        self.log.info('rebooted', device_id=self.device_id)

    def self_test_device(self, device):
        """
        This is called to Self a device based on a NBI call.
        :param device: A Voltha.Device object.
        :return: Will return result of self test
        """
        from voltha.protos.voltha_pb2 import SelfTestResponse
        self.log.info('self-test-device', device=device.id)
        # TODO: Support self test?
        return SelfTestResponse(result=SelfTestResponse.NOT_SUPPORTED)

    def disable(self):
        self.log.info('disabling', device_id=self.device_id)
        self.enabled = False

        # Get the latest device reference
        device = self.adapter_agent.get_device(self.device_id)

        # Disable all ports on that device
        self.adapter_agent.disable_all_ports(self.device_id)

        # Update the device operational status to UNKNOWN
        device.oper_status = OperStatus.UNKNOWN
        device.connect_status = ConnectStatus.UNREACHABLE
        device.reason = 'Disabled'
        self.adapter_agent.update_device(device)

        # Remove the uni logical port from the OLT, if still present
        parent_device = self.adapter_agent.get_device(device.parent_id)
        assert parent_device
        logical_device_id = parent_device.parent_id
        assert logical_device_id

        for uni in self.uni_ports:
            port_id = 'uni-{}'.format(uni.port_number)

            try:
                port = self.adapter_agent.get_logical_port(logical_device_id,
                                                           port_id)
                self.adapter_agent.delete_logical_port(logical_device_id, port)
            except KeyError:
                self.log.info('logical-port-not-found', device_id=self.device_id,
                              portid=port_id)

        # Remove pon port from parent
        for port in self.pon_ports:
            self.adapter_agent.delete_port_reference_from_parent(self.device_id,
                                                                 port.get_port())

        # Just updating the port status may be an option as well
        # port.ofp_port.config = OFPPC_NO_RECV
        # yield self.adapter_agent.update_logical_port(logical_device_id,
        #                                             port)
        # Unregister for proxied message
        self.adapter_agent.unregister_for_proxied_messages(
            device.proxy_address)

        # TODO:
        # 1) Remove all flows from the device
        # 2) Remove the device from ponsim

        self.log.info('disabled', device_id=device.id)

    def reenable(self):
        self.log.info('re-enabling', device_id=self.device_id)
        try:
            # Get the latest device reference
            device = self.adapter_agent.get_device(self.device_id)
            self._cancel_deferred()

            # First we verify that we got parent reference and proxy info
            assert device.parent_id
            assert device.proxy_address.device_id
            # assert device.proxy_address.channel_id

            # Re-register for proxied messages right away
            self.proxy_address = device.proxy_address
            self.adapter_agent.register_for_proxied_messages(
                device.proxy_address)

            # Re-enable the ports on that device
            self.adapter_agent.enable_all_ports(self.device_id)

            # Refresh the port reference
            # self.uni_port = self._get_uni_port()   deprecated

            # Add the pon port reference to the parent
            for port in self.pon_ports:
                # TODO: Send 'enable' to PonPort?
                self.adapter_agent.add_port_reference_to_parent(device.id,
                                                                port.get_port())

            # Update the connect status to REACHABLE
            device.connect_status = ConnectStatus.REACHABLE
            self.adapter_agent.update_device(device)

            # re-add uni port to logical device
            parent_device = self.adapter_agent.get_device(device.parent_id)
            self.logical_device_id = parent_device.parent_id
            assert self.logical_device_id, 'Invalid logical device ID'

            if self.olt_created:
                # vlan non-zero if created via legacy method (not xPON)
                self.uni_port('deprecated').add_logical_port(device.vlan, device.vlan,
                                                             control_vlan=device.vlan)

            device = self.adapter_agent.get_device(device.id)
            device.oper_status = OperStatus.ACTIVE
            device.reason = ''

            self.enabled = True
            self.adapter_agent.update_device(device)

            self.log.info('re-enabled', device_id=device.id)
        except Exception, e:
            self.log.exception('error-reenabling', e=e)
Example #3
0
    def activate(self, device):
        self.log.info('activating')

        # first we verify that we got parent reference and proxy info
        assert device.parent_id, 'Invalid Parent ID'
        assert device.proxy_address.device_id, 'Invalid Device ID'

        if device.vlan:
            # vlan non-zero if created via legacy method (not xPON). Also
            # Set a random serial number since not xPON based
            self._olt_created = True

        # register for proxied messages right away
        self.proxy_address = device.proxy_address
        self.adapter_agent.register_for_proxied_messages(device.proxy_address)

        # initialize device info
        device.root = True
        device.vendor = 'Adtran Inc.'
        device.model = 'n/a'
        device.hardware_version = 'n/a'
        device.firmware_version = 'n/a'
        device.reason = ''

        # TODO: Support more versions as needed
        images = Image(version='NOT AVAILABLE')
        device.images.image.extend([images])

        device.connect_status = ConnectStatus.UNKNOWN

        ############################################################################
        # Setup PM configuration for this device

        self.pm_metrics = OnuPmMetrics(self,
                                       device,
                                       grouped=True,
                                       freq_override=False)
        pm_config = self.pm_metrics.make_proto()
        self.log.info("initial-pm-config", pm_config=pm_config)
        self.adapter_agent.update_device_pm_config(pm_config, init=True)

        ############################################################################
        # Setup Alarm handler

        self.alarms = AdapterAlarms(self.adapter, device.id)

        # reference of uni_port is required when re-enabling the device if
        # it was disabled previously
        # Need to query ONU for number of supported uni ports
        # For now, temporarily set number of ports to 1 - port #2

        # Register physical ports.  Should have at least one of each

        pon_port = PonPort.create(self, self._next_port_number)

        self._pons[pon_port.port_number] = pon_port
        self.adapter_agent.add_port(device.id, pon_port.get_port())

        parent_device = self.adapter_agent.get_device(device.parent_id)
        self.logical_device_id = parent_device.parent_id
        assert self.logical_device_id, 'Invalid logical device ID'

        if self._olt_created:
            # vlan non-zero if created via legacy method (not xPON). Also
            # Set a random serial number since not xPON based

            uni_port = UniPort.create(self, self._next_port_number,
                                      'deprecated', device.vlan)
            self._unis[uni_port.port_number] = uni_port
            self.adapter_agent.add_port(device.id, uni_port.get_port())

            device.serial_number = uuid4().hex
            uni_port.add_logical_port(device.vlan, control_vlan=device.vlan)

            # Start things up for this ONU Handler.
            self.enabled = True

        # Start collecting stats from the device after a brief pause
        reactor.callLater(30, self.start_kpi_collection, device.id)

        self.adapter_agent.update_device(device)
    def activate(self, device):
        self.log.info('activating')

        # first we verify that we got parent reference and proxy info
        assert device.parent_id, 'Invalid Parent ID'
        assert device.proxy_address.device_id, 'Invalid Device ID'

        if device.vlan:
            # vlan non-zero if created via legacy method (not xPON). Also
            # Set a random serial number since not xPON based
            self._olt_created = True

        # register for proxied messages right away
        self.proxy_address = device.proxy_address
        self.adapter_agent.register_for_proxied_messages(device.proxy_address)

        # initialize device info
        device.root = True
        device.vendor = 'Adtran Inc.'
        device.model = 'n/a'
        device.hardware_version = 'n/a'
        device.firmware_version = 'n/a'
        device.reason = ''

        # TODO: Support more versions as needed
        images = Image(version='NOT AVAILABLE')
        device.images.image.extend([images])

        device.connect_status = ConnectStatus.UNKNOWN

        ############################################################################
        # Setup PM configuration for this device

        self.pm_metrics = OnuPmMetrics(self, device, grouped=True, freq_override=False)
        pm_config = self.pm_metrics.make_proto()
        self.log.info("initial-pm-config", pm_config=pm_config)
        self.adapter_agent.update_device_pm_config(pm_config, init=True)

        ############################################################################
        # Setup Alarm handler

        self.alarms = AdapterAlarms(self.adapter, device.id)

        # reference of uni_port is required when re-enabling the device if
        # it was disabled previously
        # Need to query ONU for number of supported uni ports
        # For now, temporarily set number of ports to 1 - port #2

        # Register physical ports.  Should have at least one of each

        pon_port = PonPort.create(self, self._next_port_number)

        self._pons[pon_port.port_number] = pon_port
        self.adapter_agent.add_port(device.id, pon_port.get_port())

        parent_device = self.adapter_agent.get_device(device.parent_id)
        self.logical_device_id = parent_device.parent_id
        assert self.logical_device_id, 'Invalid logical device ID'

        if self._olt_created:
            # vlan non-zero if created via legacy method (not xPON). Also
            # Set a random serial number since not xPON based

            uni_port = UniPort.create(self, self._next_port_number,
                                      'deprecated', device.vlan)
            self._unis[uni_port.port_number] = uni_port
            self.adapter_agent.add_port(device.id, uni_port.get_port())

            device.serial_number = uuid4().hex
            uni_port.add_logical_port(device.vlan, control_vlan=device.vlan)

            # Start things up for this ONU Handler.
            self.enabled = True

        # Start collecting stats from the device after a brief pause
        reactor.callLater(30, self.start_kpi_collection, device.id)

        self.adapter_agent.update_device(device)