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