def setUp(self): registry.register( 'core', VolthaCore(instance_id=1, core_store_id=1, grpc_port=50060, version="1", log_level=LogLevel.INFO)).start() self.adapter_agent_ont = AdapterAgent("broadcom_onu", "BroadcomOnuAdapter") self.adapter_agent_olt = AdapterAgent("asfvolt16_olt", "Asfvolt16Adapter") # create and update the core with Broadcom ONU device type self.onu_device_type = DeviceType(id='broadcom_onu', vendor_id='BRCM', adapter='broadcom_onu', accepts_bulk_flow_update=True) # create and update the core with Broadcom ONU device type self.olt_device_type = DeviceType(id='asfvolt16_olt', vendor_id='Edgecore', adapter='asfvolt16_olt', accepts_bulk_flow_update=True) self.adapter_agent_ont._make_up_to_date('/device_types', 'broadcom_onu', self.onu_device_type) self.adapter_agent_olt._make_up_to_date('/device_types', 'asfvolt16_olt', self.olt_device_type)
def __init__(self, adapter_agent, config, device_handler_class, name, vendor, version, device_type, vendor_id, accepts_bulk_flow_update=True, accepts_add_remove_flow_updates=False): log.debug('Initializing adapter: {} {} {}'.format( vendor, name, version)) self.adapter_agent = adapter_agent self.config = config self.name = name self.supported_device_types = [ DeviceType( id=device_type, vendor_id=vendor_id, adapter=name, accepts_bulk_flow_update=accepts_bulk_flow_update, accepts_add_remove_flow_updates=accepts_add_remove_flow_updates ) ] self.descriptor = Adapter( id=self.name, vendor=vendor, version=version, config=AdapterConfig(log_level=LogLevel.INFO)) self.devices_handlers = dict() # device_id -> Olt/OnuHandler() self.device_handler_class = device_handler_class
class TellabsOpenomciOnuAdapter(BrcmOpenomciOnuAdapter): name = 'tellabs_openomci_onu' supported_device_types = [ DeviceType( id=name, vendor_ids=['BFWS', 'TSLS', 'IPHO', 'SHGJ'], adapter=name, accepts_bulk_flow_update=True ) ] def __init__(self, adapter_agent, config): super(TellabsOpenomciOnuAdapter, self).__init__(adapter_agent, config) self.descriptor = Adapter( id=self.name, vendor='Tellabs Inc.', version='0.1', config=AdapterConfig(log_level=LogLevel.INFO) ) log.info('tellabs_openomci_onu.__init__', adapter=self.descriptor) self.broadcom_omci['mib-synchronizer']['database'] = MibDbExternal def device_types(self): return DeviceTypes(items=self.supported_device_types) def custom_me_entities(self): return onu_custom_me_entities()
class BBSimOltAdapter(OpenoltAdapter): name = 'bbsimolt' supported_device_types = [ DeviceType(id=name, adapter=name, accepts_bulk_flow_update=True, accepts_direct_logical_flows_update=True) ] def __init__(self, adapter_agent, config): super(BBSimOltAdapter, self).__init__(adapter_agent, config) # overwrite the descriptor self.descriptor = Adapter( id=self.name, vendor='CORD', version='0.1', config=AdapterConfig(log_level=LogLevel.INFO)) self.bbsim_id = 17 #TODO: This should be modified later def adopt_device(self, device): self.bbsim_id += 1 log.info('adopt-device', device=device) support_classes = deepcopy(OpenOltDefaults)['support_classes'] # Customize platform support_classes['platform'] = BBSimOltPlatform support_classes['flow_mgr'] = BBSimOltFlowMgr support_classes['alarm_mgr'] = BBSimOltAlarmMgr support_classes['stats_mgr'] = BBSimOltStatisticsMgr support_classes['bw_mgr'] = BBSimOltBW kwargs = { 'support_classes': support_classes, 'adapter_agent': self.adapter_agent, 'device': device, 'device_num': self.num_devices + 1, 'dp_id': '00:00:00:00:00:' + hex(self.bbsim_id)[-2:] } try: self.devices[device.id] = BBSimOltDevice(**kwargs) except Exception as e: log.error('Failed to adopt BBSimOLT device', error=e) del self.devices[device.id] raise else: self.num_devices += 1 def delete_device(self, device): self.bbsim_id -= 1 super(BBSimOltAdapter, self).delete_device(device) self.num_devices -= 1
class CigOpenomciOnuAdapter(BrcmOpenomciOnuAdapter): name = 'cig_openomci_onu' supported_device_types = [ DeviceType( id=name, #vendor_ids=[], vendor_ids=['CIGG'], adapter=name, accepts_bulk_flow_update=True) ] def __init__(self, adapter_agent, config): super(CigOpenomciOnuAdapter, self).__init__(adapter_agent, config) # self.adapter_agent = adapter_agent # self.config = config self.descriptor = Adapter( id=self.name, vendor='CIG Tech', version='0.10', config=AdapterConfig(log_level=LogLevel.INFO)) # self.devices_handlers = dict() # Customize OpenOMCI for CIG ONUs self._omci_support_cls = deepcopy(OpenOmciAgentDefaults) # self.broadcom_omci['mib-synchronizer']['state-machine'] = BrcmMibSynchronizer # self.broadcom_omci['mib-synchronizer']['database'] = MibDbVolatileDict # self.broadcom_omci['omci-capabilities']['tasks']['get-capabilities'] = BrcmCapabilitiesTask # self._omci_agent = OpenOMCIAgent(self.adapter_agent.core, # support_classes=self.broadcom_omci) self._omci_agent = OpenOMCIAgent( self.adapter_agent.core, support_classes=self._omci_support_cls) # register for adapter messages # self.adapter_agent.register_for_inter_adapter_messages() def device_types(self): return DeviceTypes(items=self.supported_device_types) def adapter_descriptor(self): return self.descriptor def device_types(self): return DeviceTypes(items=self.supported_device_types)
class TellabsAdapter(OpenoltAdapter): name = 'tellabs_olt' supported_device_types = [ DeviceType( id=name, adapter=name, accepts_bulk_flow_update=True, accepts_direct_logical_flows_update=True ) ] def __init__(self, adapter_agent, config): super(TellabsAdapter, self).__init__(adapter_agent, config) # overwrite the descriptor self.descriptor = Adapter( id=self.name, vendor='Tellabs Inc.', version='0.1', config=AdapterConfig(log_level=LogLevel.INFO) ) log.info('tellabs_olt.__init__', adapter=self.descriptor) def adopt_device(self, device): log.info('adopt-device', device=device) support_classes = deepcopy(OpenOltDefaults)['support_classes'] # Customize resource_mgr support_classes['resource_mgr'] = TellabsResourceManager kwargs = { 'support_classes': support_classes, 'adapter_agent': self.adapter_agent, 'device': device, 'device_num': self.num_devices + 1 } try: self.devices[device.id] = OpenoltDevice(**kwargs) except Exception as e: log.error('Failed to adopt OpenOLT device', error=e) del self.devices[device.id] raise else: self.num_devices += 1
class SimulatedOnuAdapter(object): name = 'simulated_onu' supported_device_types = [ DeviceType(id='simulated_onu', adapter=name, accepts_bulk_flow_update=True) ] def __init__(self, adapter_agent, config): self.adapter_agent = adapter_agent self.config = config self.descriptor = Adapter( id=self.name, vendor='Voltha project', version='0.1', config=AdapterConfig(log_level=LogLevel.INFO)) self.incoming_messages = DeferredQueue() def start(self): log.debug('starting') log.info('started') def stop(self): log.debug('stopping') log.info('stopped') def adapter_descriptor(self): return self.descriptor def device_types(self): return DeviceTypes(items=self.supported_device_types) def health(self): return HealthStatus(state=HealthStatus.HealthState.HEALTHY) def change_master_state(self, master): raise NotImplementedError() def adopt_device(self, device): # We kick of a simulated activation scenario reactor.callLater(0.2, self._simulate_device_activation, device) return device def reconcile_device(self, device): raise NotImplementedError() def abandon_device(self, device): raise NotImplementedError() def disable_device(self, device): device.oper_status = OperStatus.UNKNOWN self.adapter_agent.update_device(device) def reenable_device(self, device): raise NotImplementedError() def reboot_device(self, device): raise NotImplementedError() def download_image(self, device, request): raise NotImplementedError() def get_image_download_status(self, device, request): raise NotImplementedError() def cancel_image_download(self, device, request): raise NotImplementedError() def activate_image_update(self, device, request): raise NotImplementedError() def revert_image_update(self, device, request): raise NotImplementedError() def delete_device(self, device): raise NotImplementedError() def get_device_details(self, device): raise NotImplementedError() def update_pm_config(self, device, pm_configs): raise NotImplementedError() def self_test_device(self, device): log.info("run-self-test-on-device", device=device.id) result = SelfTestResponse(result=random.choice([ SelfTestResponse.SUCCESS, SelfTestResponse.FAILURE, SelfTestResponse.NOT_SUPPORTED, SelfTestResponse.UNKNOWN_ERROR ])) return result @inlineCallbacks def _simulate_device_activation(self, device): # 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 # we pretend that we were able to contact the device and obtain # additional information about it device.vendor = 'simulated onu adapter' device.model = 'n/a' device.hardware_version = 'n/a' device.firmware_version = 'n/a' device.serial_number = uuid4().hex device.connect_status = ConnectStatus.REACHABLE image1 = Image(name="onu_candidate1", version="1.0", hash="1234567892", install_datetime=datetime.datetime.utcnow().isoformat(), is_active=True, is_committed=True, is_valid=True) image2 = Image(name="onu_candidate2", version="1.0", hash="1234567893", install_datetime=datetime.datetime.utcnow().isoformat(), is_active=False, is_committed=False, is_valid=True) device.images.image.extend([image1, image2]) self.adapter_agent.update_device(device) # then shortly after we create some ports for the device yield asleep(0.05) uni_port = Port(port_no=2, label='UNI facing Ethernet port', type=Port.ETHERNET_UNI, admin_state=AdminState.ENABLED, oper_status=OperStatus.ACTIVE) self.adapter_agent.add_port(device.id, uni_port) self.adapter_agent.add_port( device.id, Port(port_no=1, label='PON port', type=Port.PON_ONU, admin_state=AdminState.ENABLED, oper_status=OperStatus.ACTIVE, peers=[ Port.PeerPort(device_id=device.parent_id, port_no=device.parent_port_no) ])) # TODO adding vports to the logical device shall be done by agent? # then we create the logical device port that corresponds to the UNI # port of the device yield asleep(0.05) # obtain logical device id parent_device = self.adapter_agent.get_device(device.parent_id) logical_device_id = parent_device.parent_id assert logical_device_id # we are going to use the proxy_address.channel_id as unique number # and name for the virtual ports, as this is guaranteed to be unique # in the context of the OLT port, so it is also unique in the context # of the logical device port_no = device.proxy_address.channel_id cap = OFPPF_1GB_FD | OFPPF_FIBER self.adapter_agent.add_logical_port( logical_device_id, LogicalPort(id=str(port_no), ofp_port=ofp_port(port_no=port_no, hw_addr=mac_str_to_tuple( '00:00:00:00:00:%02x' % port_no), name='uni-{}'.format(port_no), config=0, state=OFPPS_LIVE, curr=cap, advertised=cap, peer=cap, curr_speed=OFPPF_1GB_FD, max_speed=OFPPF_1GB_FD), device_id=device.id, device_port_no=uni_port.port_no)) # simulate a proxied message sending and receving a reply reply = yield self._simulate_message_exchange(device) # and finally update to "ACTIVE" device = self.adapter_agent.get_device(device.id) device.oper_status = OperStatus.ACTIVE self.adapter_agent.update_device(device) def update_flows_bulk(self, device, flows, groups): log.debug('bulk-flow-update', device_id=device.id, flows=flows, groups=groups) # sample code that analyzes the incoming flow table assert len(groups.items) == 0, "Cannot yet deal with groups" for flow in flows.items: in_port = get_in_port(flow) assert in_port is not None if in_port == 2: # Downstream rule for field in get_ofb_fields(flow): if field.type == ETH_TYPE: _type = field.eth_type pass # construct ether type based condition here elif field.type == IP_PROTO: _proto = field.ip_proto pass # construct ip_proto based condition here elif field.type == IN_PORT: _port = field.port pass # construct in_port based condition here elif field.type == VLAN_VID: _vlan_vid = field.vlan_vid pass # construct VLAN ID based filter condition here elif field.type == VLAN_PCP: _vlan_pcp = field.vlan_pcp pass # construct VLAN PCP based filter condition here # TODO else: raise NotImplementedError('field.type={}'.format( field.type)) for action in get_actions(flow): if action.type == OUTPUT: pass # construct packet emit rule here elif action.type == PUSH_VLAN: if action.push.ethertype != 0x8100: log.error('unhandled-ether-type', ethertype=action.push.ethertype) pass # construct vlan push command here elif action.type == POP_VLAN: pass # construct vlan pop command here elif action.type == SET_FIELD: assert (action.set_field.field.oxm_class == ofp.OFPXMC_OPENFLOW_BASIC) field = action.set_field.field.ofb_field if field.type == VLAN_VID: pass # construct vlan_id set command here else: log.error('unsupported-action-set-field-type', field_type=field.type) else: log.error('unsupported-action-type', action_type=action.type) # final assembly of low level device flow rule and pushing it # down to device pass elif in_port == 1: # Upstream rule for field in get_ofb_fields(flow): if field.type == ETH_TYPE: _type = field.eth_type pass # construct ether type based condition here elif field.type == IP_PROTO: _proto = field.ip_proto pass # construct ip_proto based condition here elif field.type == IN_PORT: _port = field.port pass # construct in_port based condition here elif field.type == VLAN_VID: _vlan_vid = field.vlan_vid pass # construct VLAN ID based filter condition here elif field.type == VLAN_PCP: _vlan_pcp = field.vlan_pcp pass # construct VLAN PCP based filter condition here elif field.type == IPV4_DST: _ipv4_dst = field.ipv4_dst pass # construct IPv4 DST address based condition elif field.type == UDP_SRC: _udp_src = field.udp_src pass # construct UDP SRC based filter here elif field.type == UDP_DST: _udp_dst = field.udp_dst pass # construct UDP DST based filter here # TODO else: raise NotImplementedError('field.type={}'.format( field.type)) for action in get_actions(flow): if action.type == OUTPUT: pass # construct packet emit rule here elif action.type == PUSH_VLAN: if action.push.ethertype != 0x8100: log.error('unhandled-ether-type', ethertype=action.push.ethertype) pass # construct vlan push command here elif action.type == SET_FIELD: assert (action.set_field.field.oxm_class == ofp.OFPXMC_OPENFLOW_BASIC) field = action.set_field.field.ofb_field if field.type == VLAN_VID: pass # construct vlan_id set command here else: log.error('unsupported-action-set-field-type', field_type=field.type) else: log.error('unsupported-action-type', action_type=action.type) # final assembly of low level device flow rule and pushing it # down to device pass else: raise Exception('Port should be 1 or 2 by our convention') def update_flows_incrementally(self, device, flow_changes, group_changes): raise NotImplementedError() def send_proxied_message(self, proxy_address, msg): raise NotImplementedError() def receive_proxied_message(self, proxy_address, msg): # just place incoming message to a list self.incoming_messages.put((proxy_address, msg)) def create_interface(self, device, data): raise NotImplementedError() def update_interface(self, device, data): raise NotImplementedError() def remove_interface(self, device, data): raise NotImplementedError() def receive_onu_detect_state(self, device_id, state): raise NotImplementedError() def create_tcont(self, device, tcont_data, traffic_descriptor_data): raise NotImplementedError() def update_tcont(self, device, tcont_data, traffic_descriptor_data): raise NotImplementedError() def remove_tcont(self, device, tcont_data, traffic_descriptor_data): raise NotImplementedError() def create_gemport(self, device, data): raise NotImplementedError() def update_gemport(self, device, data): raise NotImplementedError() def remove_gemport(self, device, data): raise NotImplementedError() def create_multicast_gemport(self, device, data): raise NotImplementedError() def update_multicast_gemport(self, device, data): raise NotImplementedError() def remove_multicast_gemport(self, device, data): raise NotImplementedError() def create_multicast_distribution_set(self, device, data): raise NotImplementedError() def update_multicast_distribution_set(self, device, data): raise NotImplementedError() def remove_multicast_distribution_set(self, device, data): raise NotImplementedError() def suppress_alarm(self, filter): raise NotImplementedError() def unsuppress_alarm(self, filter): raise NotImplementedError() @inlineCallbacks def _simulate_message_exchange(self, device): # register for receiving async messages self.adapter_agent.register_for_proxied_messages(device.proxy_address) # reset incoming message queue while self.incoming_messages.pending: _ = yield self.incoming_messages.get() # construct message msg = 'test message' # send message self.adapter_agent.send_proxied_message(device.proxy_address, msg) # wait till we detect incoming message yield self.incoming_messages.get() # by returning we allow the device to be shown as active, which # indirectly verified that message passing works def receive_packet_out(self, logical_device_id, egress_port_no, msg): log.info('packet-out', logical_device_id=logical_device_id, egress_port_no=egress_port_no, msg_len=len(msg)) def receive_inter_adapter_message(self, msg): raise NotImplementedError()
class AdtranOltAdapter(object): name = 'adtran_olt' supported_device_types = [ DeviceType( id=name, adapter=name, accepts_bulk_flow_update=True, accepts_add_remove_flow_updates=False # TODO: Support flow-mods ) ] def __init__(self, adapter_agent, config): self.adapter_agent = adapter_agent self.config = config self.descriptor = Adapter( id=self.name, vendor='Adtran Inc.', version='0.17', config=AdapterConfig(log_level=LogLevel.INFO)) log.debug('adtran_olt.__init__', adapter_agent=adapter_agent) self.devices_handlers = dict() # device_id -> AdtranOltHandler() self.interface = registry('main').get_args().interface self.logical_device_id_to_root_device_id = dict() def start(self): """ Called once after adapter instance is loaded. Can be used to async initialization. :return: (None or Deferred) """ log.info('started', interface=self.interface) def stop(self): """ Called once before adapter is unloaded. It can be used to perform any cleanup after the adapter. :return: (None or Deferred) """ log.info('stopped', interface=self.interface) def adapter_descriptor(self): """ Return the adapter descriptor object for this adapter. :return: voltha.Adapter grpc object (see voltha/protos/adapter.proto), with adapter-specific information and config extensions. """ log.debug('get descriptor', interface=self.interface) return self.descriptor def device_types(self): """ Return list of device types supported by the adapter. :return: voltha.DeviceTypes protobuf object, with optional type specific extensions. """ log.debug('get device_types', interface=self.interface, items=self.supported_device_types) return DeviceTypes(items=self.supported_device_types) def health(self): """ Return a 3-state health status using the voltha.HealthStatus message. :return: Deferred or direct return with voltha.HealthStatus message """ log.debug('get health', interface=self.interface) return HealthStatus(state=HealthStatus.HealthState.HEALTHY) def change_master_state(self, master): """ Called to indicate if plugin shall assume or lose master role. The master role can be used to perform functions that must be performed from a single point in the cluster. In single-node deployments of Voltha, the plugins are always in master role. :param master: (bool) True to indicate the mastership needs to be assumed; False to indicate that mastership needs to be abandoned. :return: (Deferred) which is fired by the adapter when mastership is assumed/dropped, respectively. """ log.debug('change_master_state', interface=self.interface, master=master) raise NotImplementedError() def adopt_device(self, device): """ Make sure the adapter looks after given device. Called when a device is provisioned top-down and needs to be activated by the adapter. :param device: A voltha.Device object, with possible device-type specific extensions. Such extensions shall be described as part of the device type specification returned by device_types(). :return: (Deferred) Shall be fired to acknowledge device ownership. """ log.info('adopt-device', device=device) kwargs = {'adapter': self, 'device-id': device.id} self.devices_handlers[device.id] = AdtranOltHandler(**kwargs) d = defer.Deferred() reactor.callLater(0, self.devices_handlers[device.id].activate, device, done_deferred=d) return d def reconcile_device(self, device): """ Make sure the adapter looks after given device. Called when this device has changed ownership from another Voltha instance to this one (typically, this occurs when the previous voltha instance went down). :param device: A voltha.Device object, with possible device-type specific extensions. Such extensions shall be described as part of the device type specification returned by device_types(). :return: (Deferred) Shall be fired to acknowledge device ownership. """ log.info('reconcile-device', device=device) kwargs = {'adapter': self, 'device-id': device.id} self.devices_handlers[device.id] = AdtranOltHandler(**kwargs) d = defer.Deferred() reactor.callLater(0, self.devices_handlers[device.id].activate, device, done_deferred=d, reconciling=True) return d def abandon_device(self, device): """ Make sure the adapter no longer looks after device. This is called if device ownership is taken over by another Voltha instance. :param device: A Voltha.Device object. :return: (Deferred) Shall be fired to acknowledge abandonment. """ log.info('abandon-device', device=device) raise NotImplementedError() def disable_device(self, device): """ This is called when a previously enabled device needs to be disabled based on a NBI call. :param device: A Voltha.Device object. :return: (Deferred) Shall be fired to acknowledge disabling the device. """ log.info('disable-device', device=device) handler = self.devices_handlers.get(device.id) if handler is not None: reactor.callLater(0, handler.disable) return device def reenable_device(self, device): """ This is called when a previously disabled device needs to be enabled based on a NBI call. :param device: A Voltha.Device object. :return: (Deferred) Shall be fired to acknowledge re-enabling the device. """ log.info('reenable-device', device=device) handler = self.devices_handlers.get(device.id) if handler is not None: d = defer.Deferred() reactor.callLater(0, handler.reenable, done_deferred=d) return d def reboot_device(self, device): """ This is called to reboot a device based on a NBI call. The admin state of the device will not change after the reboot :param device: A Voltha.Device object. :return: (Deferred) Shall be fired to acknowledge the reboot. """ log.info('reboot_device', device=device) handler = self.devices_handlers.get(device.id) if handler is not None: reactor.callLater(0, handler.reboot) return device def download_image(self, device, request): """ This is called to request downloading a specified image into the standby partition of a device based on a NBI call. :param device: A Voltha.Device object. :param request: A Voltha.ImageDownload object. :return: (Deferred) Shall be fired to acknowledge the download. """ log.info('image_download', device=device, request=request) handler = self.devices_handlers.get(device.id) if handler is not None: return handler.start_download(device, request, defer.Deferred()) def get_image_download_status(self, device, request): """ This is called to inquire about a requested image download status based on a NBI call. The adapter is expected to update the DownloadImage DB object with the query result :param device: A Voltha.Device object. :param request: A Voltha.ImageDownload object. :return: (Deferred) Shall be fired to acknowledge """ log.info('get_image_download', device=device, request=request) handler = self.devices_handlers.get(device.id) if handler is not None: return handler.download_status(device, request, defer.Deferred()) def cancel_image_download(self, device, request): """ This is called to cancel a requested image download based on a NBI call. The admin state of the device will not change after the download. :param device: A Voltha.Device object. :param request: A Voltha.ImageDownload object. :return: (Deferred) Shall be fired to acknowledge """ log.info('cancel_image_download', device=device) handler = self.devices_handlers.get(device.id) if handler is not None: return handler.cancel_download(device, request, defer.Deferred()) def activate_image_update(self, device, request): """ This is called to activate a downloaded image from a standby partition into active partition. Depending on the device implementation, this call may or may not cause device reboot. If no reboot, then a reboot is required to make the activated image running on device This call is expected to be non-blocking. :param device: A Voltha.Device object. :param request: A Voltha.ImageDownload object. :return: (Deferred) OperationResponse object. """ log.info('activate_image_update', device=device, request=request) handler = self.devices_handlers.get(device.id) if handler is not None: return handler.activate_image(device, request, defer.Deferred()) def revert_image_update(self, device, request): """ This is called to deactivate the specified image at active partition, and revert to previous image at standby partition. Depending on the device implementation, this call may or may not cause device reboot. If no reboot, then a reboot is required to make the previous image running on device This call is expected to be non-blocking. :param device: A Voltha.Device object. :param request: A Voltha.ImageDownload object. :return: (Deferred) OperationResponse object. """ log.info('revert_image_update', device=device, request=request) handler = self.devices_handlers.get(device.id) if handler is not None: return handler.revert_image(device, request, defer.Deferred()) 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 log.info('self-test-device', device=device.id) # TODO: Support self test? return SelfTestResponse(result=SelfTestResponse.NOT_SUPPORTED) def delete_device(self, device): """ This is called to delete a device from the PON based on a NBI call. If the device is an OLT then the whole PON will be deleted. :param device: A Voltha.Device object. :return: (Deferred) Shall be fired to acknowledge the deletion. """ log.info('delete-device', device=device) handler = self.devices_handlers.get(device.id) if handler is not None: reactor.callLater(0, handler.delete) return device def get_device_details(self, device): """ This is called to get additional device details based on a NBI call. :param device: A Voltha.Device object. :return: (Deferred) Shall be fired to acknowledge the retrieval of additional details. """ log.debug('get_device_details', device=device) raise NotImplementedError() def update_flows_bulk(self, device, flows, groups): """ Called after any flow table change, but only if the device supports bulk mode, which is expressed by the 'accepts_bulk_flow_update' capability attribute of the device type. :param device: A Voltha.Device object. :param flows: An openflow_v13.Flows object :param groups: An openflow_v13.Flows object :return: (Deferred or None) """ log.debug('bulk-flow-update', device_id=device.id, flows=flows, groups=groups, num_flows=len(flows.items)) assert len(groups.items) == 0, "Cannot yet deal with groups" handler = self.devices_handlers.get(device.id) if handler is not None: return handler.update_flow_table(flows.items, device) def update_flows_incrementally(self, device, flow_changes, group_changes): """ [This mode is not supported yet.] :param device: A Voltha.Device object. :param flow_changes: :param group_changes: :return: """ log.debug('update_flows_incrementally', device=device, flow_changes=flow_changes, group_changes=group_changes) raise NotImplementedError() def update_pm_config(self, device, pm_configs): """ Called every time a request is made to change pm collection behavior :param device: A Voltha.Device object :param pm_configs: A Pms """ log.debug('update_pm_config', device=device, pm_configs=pm_configs) handler = self.devices_handlers.get(device.id) if handler is not None: handler.update_pm_config(device, pm_configs) def send_proxied_message(self, proxy_address, msg): """ Forward a msg to a child device of device, addressed by the given proxy_address=Device.ProxyAddress(). :param proxy_address: Address info for the parent device to route the message to the child device. This was given to the child device by the parent device at the creation of the child device. :param msg: (str) The actual message to send. :return: (Deferred(None) or None) The return of this method should indicate that the message was successfully *sent*. """ log.debug('send-proxied-message', proxy_address=proxy_address, msg=msg) handler = self.devices_handlers.get(proxy_address.device_id) if handler is not None: handler.send_proxied_message(proxy_address, msg) def receive_proxied_message(self, proxy_address, msg): """ Pass an async message (arrived via a proxy) to this device. :param proxy_address: Address info for the parent device to route the message to the child device. This was given to the child device by the parent device at the creation of the child device. Note this is the proxy_address with which the adapter had to register prior to receiving proxied messages. :param msg: (str) The actual message received. :return: None """ log.debug('receive_proxied_message', proxy_address=proxy_address, msg=msg) raise NotImplementedError() def receive_packet_out(self, logical_device_id, egress_port_no, msg): """ Pass a packet_out message content to adapter so that it can forward it out to the device. This is only called on root devices. :param logical_device_id: :param egress_port_no: egress logical port number :param msg: actual message :return: None """ log.debug('packet-out', logical_device_id=logical_device_id, egress_port_no=egress_port_no, msg_len=len(msg)) def ldi_to_di(ldi): di = self.logical_device_id_to_root_device_id.get(ldi) if di is None: logical_device = self.adapter_agent.get_logical_device(ldi) di = logical_device.root_device_id self.logical_device_id_to_root_device_id[ldi] = di return di device_id = ldi_to_di(logical_device_id) handler = self.devices_handlers.get(device_id) if handler is not None: handler.packet_out(egress_port_no, msg) def receive_inter_adapter_message(self, msg): """ Called when the adapter receives a message that was sent to it directly from another adapter. An adapter may register for these messages by calling the register_for_inter_adapter_messages() method in the adapter agent. Note that it is the responsibility of the sending and receiving adapters to properly encode and decode the message. :param msg: The message contents. :return: None """ log.info('rx_inter_adapter_msg') raise NotImplementedError() def suppress_alarm(self, filter): """ Inform an adapter that all incoming alarms should be suppressed :param filter: A Voltha.AlarmFilter object. :return: (Deferred) Shall be fired to acknowledge the suppression. """ log.info('suppress_alarm', filter=filter) raise NotImplementedError() def unsuppress_alarm(self, filter): """ Inform an adapter that all incoming alarms should resume :param filter: A Voltha.AlarmFilter object. :return: (Deferred) Shall be fired to acknowledge the unsuppression. """ log.info('unsuppress_alarm', filter=filter) raise NotImplementedError() # PON Mgnt APIs # def create_interface(self, device, data): """ API to create various interfaces (only some PON interfaces as of now) in the devices """ log.debug('create-interface', data=data) handler = self.devices_handlers.get(device.id) if handler is not None: handler.xpon_create(data) def update_interface(self, device, data): """ API to update various interfaces (only some PON interfaces as of now) in the devices """ log.debug('update-interface', data=data) handler = self.devices_handlers.get(device.id) if handler is not None: handler.xpon_update(data) def remove_interface(self, device, data): """ API to delete various interfaces (only some PON interfaces as of now) in the devices """ log.debug('remove-interface', data=data) handler = self.devices_handlers.get(device.id) if handler is not None: handler.xpon_remove(data) def receive_onu_detect_state(self, proxy_address, state): """ Receive onu detect state in ONU adapter :param proxy_address: ONU device address :param state: ONU detect state (bool) :return: None """ raise NotImplementedError() def create_tcont(self, device, tcont_data, traffic_descriptor_data): """ API to create tcont object in the devices :param device: device id :param tcont_data: tcont data object :param traffic_descriptor_data: traffic descriptor data object :return: None """ log.info('create-tcont', tcont_data=tcont_data, traffic_descriptor_data=traffic_descriptor_data) handler = self.devices_handlers.get(device.id) if handler is not None: handler.create_tcont(tcont_data, traffic_descriptor_data) def update_tcont(self, device, tcont_data, traffic_descriptor_data): """ API to update tcont object in the devices :param device: device id :param tcont_data: tcont data object :param traffic_descriptor_data: traffic descriptor data object :return: None """ log.info('update-tcont', tcont_data=tcont_data, traffic_descriptor_data=traffic_descriptor_data) handler = self.devices_handlers.get(device.id) if handler is not None: handler.update_tcont(tcont_data, traffic_descriptor_data) def remove_tcont(self, device, tcont_data, traffic_descriptor_data): """ API to delete tcont object in the devices :param device: device id :param tcont_data: tcont data object :param traffic_descriptor_data: traffic descriptor data object :return: None """ log.info('remove-tcont', tcont_data=tcont_data, traffic_descriptor_data=traffic_descriptor_data) handler = self.devices_handlers.get(device.id) if handler is not None: handler.remove_tcont(tcont_data, traffic_descriptor_data) def create_gemport(self, device, data): """ API to create gemport object in the devices :param device: device id :param data: gemport data object :return: None """ log.debug('create-gemport', data=data) handler = self.devices_handlers.get(device.id) if handler is not None: handler.xpon_create(data) def update_gemport(self, device, data): """ API to update gemport object in the devices :param device: device id :param data: gemport data object :return: None """ log.info('update-gemport', data=data) handler = self.devices_handlers.get(device.id) if handler is not None: handler.xpon_update(data) def remove_gemport(self, device, data): """ API to delete gemport object in the devices :param device: device id :data: gemport data object :return: None """ log.info('remove-gemport', data=data) handler = self.devices_handlers.get(device.id) if handler is not None: handler.xpon_remove(data) def create_multicast_gemport(self, device, data): """ API to create multicast gemport object in the devices :param device: device id :data: multicast gemport data object :return: None """ log.info('create-mcast-gemport', data=data) handler = self.devices_handlers.get(device.id) if handler is not None: handler.xpon_create(data) def update_multicast_gemport(self, device, data): """ API to update multicast gemport object in the devices :param device: device id :data: multicast gemport data object :return: None """ log.info('update-mcast-gemport', data=data) handler = self.devices_handlers.get(device.id) if handler is not None: handler.xpon_update(data) def remove_multicast_gemport(self, device, data): """ API to delete multicast gemport object in the devices :param device: device id :data: multicast gemport data object :return: None """ log.info('remove-mcast-gemport', data=data) handler = self.devices_handlers.get(device.id) if handler is not None: handler.xpon_remove(data) def create_multicast_distribution_set(self, device, data): """ API to create multicast distribution rule to specify the multicast VLANs that ride on the multicast gemport :param device: device id :data: multicast distribution data object :return: None """ log.info('create-mcast-distribution-set', data=data) handler = self.devices_handlers.get(device.id) if handler is not None: handler.xpon_create(data) def update_multicast_distribution_set(self, device, data): """ API to update multicast distribution rule to specify the multicast VLANs that ride on the multicast gemport :param device: device id :data: multicast distribution data object :return: None """ log.info('update-mcast-distribution-set', data=data) handler = self.devices_handlers.get(device.id) if handler is not None: handler.xpon_update(data) def remove_multicast_distribution_set(self, device, data): """ API to delete multicast distribution rule to specify the multicast VLANs that ride on the multicast gemport :param device: device id :data: multicast distribution data object :return: None """ log.info('remove-mcast-distribution-set', data=data) handler = self.devices_handlers.get(device.id) if handler is not None: handler.xpon_remove(data)
class RubyAdapter(object): name = "microsemi_olt" supported_device_types = [ DeviceType( id=name, adapter=name, accepts_bulk_flow_update=True ) ] def __init__(self, adaptor_agent, config): self.adaptor_agent = adaptor_agent self.config = config self.olts = {} self.descriptor = Adapter( id=self.name, vendor='Microsemi / Celestica', version='0.1', config=AdapterConfig(log_level=LogLevel.INFO) ) self.interface = registry('main').get_args().interface def start(self): log.info('starting') log.info('started') return self def stop(self): log.debug('stopping') for target in self.olts.keys(): self._abandon(target) log.info('stopped') return self def adapter_descriptor(self): return self.descriptor def device_types(self): return DeviceTypes(items=self.supported_device_types) def health(self): return HealthStatus(state=HealthStatus.HealthState.HEALTHY) def change_master_state(self, master): raise NotImplementedError() def adopt_device(self, device): device_manager = DeviceManager(device, self.adaptor_agent) target = device.mac_address comm = PAS5211Communication(dst_mac=target, iface=self.interface) olt = OltStateMachine(iface=self.interface, comm=comm, target=target, device=device_manager) activation = ActivationWatcher(iface=self.interface, comm=comm, target=target, device=device_manager) reactor.callLater(0, self._init_olt, olt, activation) log.info('adopted-device', device=device) self.olts[target] = (olt, activation) def abandon_device(self, device): self._abandon(device.mac_address) def deactivate_device(self, device): self._abandon(device.mac_address) def update_flows_bulk(self, device, flows, groups): log.debug('bulk-flow-update', device_id=device.id, flows=flows, groups=groups) def send_proxied_message(self, proxy_address, msg): log.info('send-proxied-message', proxy_address=proxy_address, msg=msg) def receive_proxied_message(self, proxy_address, msg): raise NotImplementedError() def update_flows_incrementally(self, device, flow_changes, group_changes): raise NotImplementedError() def receive_packet_out(self, logical_device_id, egress_port_no, msg): log.info('packet-out', logical_device_id=logical_device_id, egress_port_no=egress_port_no, msg_len=len(msg)) ## # Private methods ## def _init_olt(self, olt, activation_watch): olt.runbg() activation_watch.runbg() def _abandon(self, target): olt, activation = self.olts[target] olt.stop() activation.stop() del self.olts[target]
class OpenoltAdapter(object): name = 'openolt' supported_device_types = [ DeviceType(id=name, adapter=name, accepts_bulk_flow_update=True, accepts_direct_logical_flows_update=True) ] def __init__(self, adapter_agent, config): self.adapter_agent = adapter_agent self.config = config self.descriptor = Adapter( id=self.name, vendor='OLT white box vendor', version='0.1', config=AdapterConfig(log_level=LogLevel.INFO)) log.debug('openolt.__init__', adapter_agent=adapter_agent) self.devices = dict() # device_id -> OpenoltDevice() self.interface = registry('main').get_args().interface self.logical_device_id_to_root_device_id = dict() self.num_devices = 0 def start(self): log.info('started', interface=self.interface) def stop(self): log.info('stopped', interface=self.interface) def adapter_descriptor(self): log.debug('get descriptor', interface=self.interface) return self.descriptor def device_types(self): log.debug('get device_types', interface=self.interface, items=self.supported_device_types) return DeviceTypes(items=self.supported_device_types) def health(self): log.debug('get health', interface=self.interface) raise NotImplementedError() def change_master_state(self, master): log.debug('change_master_state', interface=self.interface, master=master) raise NotImplementedError() def adopt_device(self, device): log.info('adopt-device', device=device) kwargs = { 'adapter_agent': self.adapter_agent, 'device': device, 'device_num': self.num_devices + 1 } try: self.devices[device.id] = OpenoltDevice(**kwargs) except Exception as e: log.error('Failed to adopt OpenOLT device', error=e) del self.devices[device.id] raise else: self.num_devices += 1 def reconcile_device(self, device): log.info('reconcile-device', device=device) kwargs = { 'adapter_agent': self.adapter_agent, 'device': device, 'device_num': self.num_devices + 1, 'reconciliation': True } try: reconciled_device = OpenoltDevice(**kwargs) log.debug('reconciled-device-recreated', device_id=reconciled_device.device_id) self.devices[device.id] = reconciled_device except Exception as e: log.error('Failed to reconcile OpenOLT device', error=e, exception_type=type(e).__name__) del self.devices[device.id] raise else: self.num_devices += 1 # Invoke the children reconciliation which would setup the # basic children data structures self.adapter_agent.reconcile_child_devices(device.id) return device def abandon_device(self, device): log.info('abandon-device', device=device) raise NotImplementedError() def disable_device(self, device): log.info('disable-device', device=device) handler = self.devices[device.id] handler.disable() def reenable_device(self, device): log.info('reenable-device', device=device) handler = self.devices[device.id] handler.reenable() def reboot_device(self, device): log.info('reboot_device', device=device) handler = self.devices[device.id] handler.reboot() def download_image(self, device, request): log.info('image_download - Not implemented yet', device=device, request=request) raise NotImplementedError() def get_image_download_status(self, device, request): log.info('get_image_download - Not implemented yet', device=device, request=request) raise NotImplementedError() def cancel_image_download(self, device, request): log.info('cancel_image_download - Not implemented yet', device=device) raise NotImplementedError() def activate_image_update(self, device, request): log.info('activate_image_update - Not implemented yet', device=device, request=request) raise NotImplementedError() def revert_image_update(self, device, request): log.info('revert_image_update - Not implemented yet', device=device, request=request) raise NotImplementedError() def self_test_device(self, device): # from voltha.protos.voltha_pb2 import SelfTestResponse log.info('Not implemented yet') raise NotImplementedError() def delete_device(self, device): log.info('delete-device', device=device) handler = self.devices[device.id] handler.delete() del self.devices[device.id] del self.logical_device_id_to_root_device_id[device.parent_id] return device def get_device_details(self, device): log.debug('get_device_details', device=device) raise NotImplementedError() def update_flows_bulk(self, device, flows, groups): log.info('bulk-flow-update', device_id=device.id, number_of_flows=len(flows.items), number_of_groups=len(groups.items)) log.debug('flows and grousp details', flows=flows, groups=groups) assert len(groups.items) == 0, "Cannot yet deal with groups" handler = self.devices[device.id] return handler.update_flow_table(flows.items) def update_flows_incrementally(self, device, flow_changes, group_changes): log.debug('update_flows_incrementally', device=device, flow_changes=flow_changes, group_changes=group_changes) log.info('This device does not allow this, therefore it is Not ' 'implemented') raise NotImplementedError() def update_logical_flows(self, device_id, flows_to_add, flows_to_remove, groups, device_rules_map): log.info('logical-flows-update', flows_to_add=len(flows_to_add), flows_to_remove=len(flows_to_remove)) log.debug('logical-flows-details', flows_to_add=flows_to_add, flows_to_remove=flows_to_remove) assert len(groups) == 0, "Cannot yet deal with groups" handler = self.devices[device_id] handler.update_logical_flows(flows_to_add, flows_to_remove, device_rules_map) def update_pm_config(self, device, pm_configs): log.info('update_pm_config - Not implemented yet', device=device, pm_configs=pm_configs) raise NotImplementedError() def send_proxied_message(self, proxy_address, msg): log.debug('send-proxied-message', proxy_address=proxy_address, msg=msg) handler = self.devices[proxy_address.device_id] handler.send_proxied_message(proxy_address, msg) def receive_proxied_message(self, proxy_address, msg): log.debug('receive_proxied_message - Not implemented', proxy_address=proxy_address, msg=msg) raise NotImplementedError() def receive_packet_out(self, logical_device_id, egress_port_no, msg): log.debug('packet-out', logical_device_id=logical_device_id, egress_port_no=egress_port_no, msg_len=len(msg)) def ldi_to_di(ldi): di = self.logical_device_id_to_root_device_id.get(ldi) if di is None: logical_device = self.adapter_agent.get_logical_device(ldi) di = logical_device.root_device_id self.logical_device_id_to_root_device_id[ldi] = di return di try: device_id = ldi_to_di(logical_device_id) handler = self.devices[device_id] handler.packet_out(egress_port_no, msg) except Exception as e: log.error('packet-out:exception', e=e.message) def receive_inter_adapter_message(self, msg): log.info('rx_inter_adapter_msg - Not implemented') raise NotImplementedError() def suppress_alarm(self, filter): log.info('suppress_alarm - Not implemented yet', filter=filter) raise NotImplementedError() def unsuppress_alarm(self, filter): log.info('unsuppress_alarm - Not implemented yet', filter=filter) raise NotImplementedError() # PON Mgnt APIs # def create_interface(self, device, data): log.debug('create-interface - Not implemented - We do not use this', data=data) raise NotImplementedError() def update_interface(self, device, data): log.debug('update-interface - Not implemented - We do not use this', data=data) raise NotImplementedError() def remove_interface(self, device, data): log.debug('remove-interface - Not implemented - We do not use this', data=data) raise NotImplementedError() def receive_onu_detect_state(self, proxy_address, state): log.debug( 'receive-onu-detect-state - Not implemented - We do not ' 'use this', proxy_address=proxy_address, state=state) raise NotImplementedError() def create_tcont(self, device, tcont_data, traffic_descriptor_data): log.info('create-tcont - Not implemented - We do not use this', tcont_data=tcont_data, traffic_descriptor_data=traffic_descriptor_data) raise NotImplementedError() def update_tcont(self, device, tcont_data, traffic_descriptor_data): log.info('update-tcont - Not implemented - We do not use this', tcont_data=tcont_data, traffic_descriptor_data=traffic_descriptor_data) raise NotImplementedError() def remove_tcont(self, device, tcont_data, traffic_descriptor_data): log.info('remove-tcont - Not implemented - We do not use this', tcont_data=tcont_data, traffic_descriptor_data=traffic_descriptor_data) raise NotImplementedError() def create_gemport(self, device, data): log.info('create-gemport - Not implemented - We do not use this', data=data) raise NotImplementedError() def update_gemport(self, device, data): log.info('update-gemport - Not implemented - We do not use this', data=data) raise NotImplementedError() def remove_gemport(self, device, data): log.info('remove-gemport - Not implemented - We do not use this', data=data) raise NotImplementedError() def create_multicast_gemport(self, device, data): log.info( 'create-mcast-gemport - Not implemented - We do not use ' 'this', data=data) raise NotImplementedError() def update_multicast_gemport(self, device, data): log.info( 'update-mcast-gemport - Not implemented - We do not use ' 'this', data=data) raise NotImplementedError() def remove_multicast_gemport(self, device, data): log.info( 'remove-mcast-gemport - Not implemented - We do not use ' 'this', data=data) raise NotImplementedError() def create_multicast_distribution_set(self, device, data): log.info( 'create-mcast-distribution-set - Not implemented - We do ' 'not use this', data=data) raise NotImplementedError() def update_multicast_distribution_set(self, device, data): log.info( 'update-mcast-distribution-set - Not implemented - We do ' 'not use this', data=data) raise NotImplementedError() def remove_multicast_distribution_set(self, device, data): log.info( 'remove-mcast-distribution-set - Not implemented - We do ' 'not use this', data=data) raise NotImplementedError() def delete_child_device(self, parent_device_id, child_device): log.info('delete-child_device', parent_device_id=parent_device_id, child_device=child_device) handler = self.devices[parent_device_id] if handler is not None: handler.delete_child_device(child_device) else: log.error('Could not find matching handler', looking_for_device_id=parent_device_id, available_handlers=self.devices.keys()) # This is currently not part of the Iadapter interface def collect_stats(self, device_id): log.info('collect_stats', device_id=device_id) handler = self.devices[device_id] if handler is not None: handler.trigger_statistics_collection() else: log.error('Could not find matching handler', looking_for_device_id=device_id, available_handlers=self.devices.keys()) def simulate_alarm(self, device, request): log.info('simulate_alarm', device=device, request=request) if device.id not in self.devices: log.error("Device does not exist", device_id=device.id) return OperationResp(code=OperationResp.OPERATION_FAILURE, additional_info="Device %s does not exist" % device.id) handler = self.devices[device.id] handler.simulate_alarm(request) return OperationResp(code=OperationResp.OPERATION_SUCCESS)
class TibitOnuAdapter(object): name = 'tibit_onu' supported_device_types = [ DeviceType(id='tibit_onu', adapter=name, accepts_bulk_flow_update=True) ] def __init__(self, adapter_agent, config): self.adapter_agent = adapter_agent self.config = config self.descriptor = Adapter( id=self.name, vendor='Tibit Communications Inc.', version='0.1', config=AdapterConfig(log_level=LogLevel.INFO)) self.incoming_messages = DeferredQueue() def start(self): log.debug('starting') log.info('started') def stop(self): log.debug('stopping') log.info('stopped') def adapter_descriptor(self): return self.descriptor def device_types(self): return DeviceTypes(items=self.supported_device_types) def health(self): return HealthStatus(state=HealthStatus.HealthState.HEALTHY) def change_master_state(self, master): raise NotImplementedError() def adopt_device(self, device): log.info('adopt-device', device=device) reactor.callLater(0.1, self._onu_device_activation, device) return device @inlineCallbacks def _onu_device_activation(self, device): # 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 # TODO: For now, pretend that we were able to contact the device and obtain # additional information about it. Should add real message. device.vendor = 'Tibit Communications, Inc.' device.model = '10G GPON ONU' device.hardware_version = 'fa161020' device.firmware_version = '16.12.02' device.software_version = '1.0' device.serial_number = uuid4().hex device.connect_status = ConnectStatus.REACHABLE self.adapter_agent.update_device(device) # then shortly after we create some ports for the device uni_port = Port(port_no=2, label='UNI facing Ethernet port', type=Port.ETHERNET_UNI, admin_state=AdminState.ENABLED, oper_status=OperStatus.ACTIVE) self.adapter_agent.add_port(device.id, uni_port) self.adapter_agent.add_port( device.id, Port(port_no=1, label='PON port', type=Port.PON_ONU, admin_state=AdminState.ENABLED, oper_status=OperStatus.ACTIVE, peers=[ Port.PeerPort(device_id=device.parent_id, port_no=device.parent_port_no) ])) # TODO adding vports to the logical device shall be done by agent? # then we create the logical device port that corresponds to the UNI # port of the device # obtain logical device id parent_device = self.adapter_agent.get_device(device.parent_id) logical_device_id = parent_device.parent_id assert logical_device_id # we are going to use the proxy_address.channel_id as unique number # and name for the virtual ports, as this is guaranteed to be unique # in the context of the OLT port, so it is also unique in the context # of the logical device port_no = device.proxy_address.channel_id cap = OFPPF_10GB_FD | OFPPF_FIBER self.adapter_agent.add_logical_port( logical_device_id, LogicalPort(id=str(port_no), ofp_port=ofp_port(port_no=port_no, hw_addr=mac_str_to_tuple( device.mac_address), name='uni-{}'.format(port_no), config=0, state=OFPPS_LIVE, curr=cap, advertised=cap, peer=cap, curr_speed=OFPPF_10GB_FD, max_speed=OFPPF_10GB_FD), device_id=device.id, device_port_no=uni_port.port_no)) # simulate a proxied message sending and receving a reply reply = yield self._message_exchange(device) # and finally update to "ACTIVE" device = self.adapter_agent.get_device(device.id) device.oper_status = OperStatus.ACTIVE self.adapter_agent.update_device(device) self.start_kpi_collection(device.id) def abandon_device(self, device): raise NotImplementedError(0) def deactivate_device(self, device): raise NotImplementedError() def update_flows_bulk(self, device, flows, groups): log.info('########################################') log.info('bulk-flow-update', device_id=device.id, flows=flows, groups=groups) assert len(groups.items) == 0, "Cannot yet deal with groups" Clause = {v: k for k, v in ClauseSubtypeEnum.iteritems()} Operator = {v: k for k, v in RuleOperatorEnum.iteritems()} for flow in flows.items: in_port = get_in_port(flow) assert in_port is not None precedence = 255 - min(flow.priority / 256, 255) if in_port == 2: log.info('#### Upstream Rule ####') dn_req = EOAMPayload(body=CablelabsOUI() / DPoEOpcode_SetRequest()) for field in get_ofb_fields(flow): if field.type == ETH_TYPE: _type = field.eth_type log.info('#### field.type == ETH_TYPE ####', field_type=_type) elif field.type == IP_PROTO: _proto = field.ip_proto log.info('#### field.type == IP_PROTO ####') elif field.type == IN_PORT: _port = field.port log.info('#### field.type == IN_PORT ####', port=_port) elif field.type == VLAN_VID: _vlan_vid = field.vlan_vid & 0xfff log.info('#### field.type == VLAN_VID ####', vlan=_vlan_vid) elif field.type == VLAN_PCP: _vlan_pcp = field.vlan_pcp log.info('#### field.type == VLAN_PCP ####', pcp=_vlan_pcp) elif field.type == UDP_DST: _udp_dst = field.udp_dst log.info('#### field.type == UDP_DST ####') elif field.type == IPV4_DST: _ipv4_dst = field.ipv4_dst log.info('#### field.type == IPV4_DST ####') else: log.info('#### field.type == NOT IMPLEMENTED!! ####') raise NotImplementedError('field.type={}'.format( field.type)) for action in get_actions(flow): if action.type == OUTPUT: log.info('#### action.type == OUTPUT ####') elif action.type == POP_VLAN: log.info('#### action.type == POP_VLAN ####') elif action.type == PUSH_VLAN: log.info('#### action.type == PUSH_VLAN ####') if action.push.ethertype != 0x8100: log.error('unhandled-tpid', ethertype=action.push.ethertype) elif action.type == SET_FIELD: log.info('#### action.type == SET_FIELD ####') assert (action.set_field.field.oxm_class == ofp.OFPXMC_OPENFLOW_BASIC) field = action.set_field.field.ofb_field if field.type == VLAN_VID: pass else: log.error('unsupported-action-set-field-type', field_type=field.type) else: log.error('UNSUPPORTED-ACTION-TYPE', action_type=action.type) elif in_port == 1: log.info('#### Downstream Rule ####') #### Loop through fields again... for field in get_ofb_fields(flow): if field.type == ETH_TYPE: _type = field.eth_type log.info('#### field.type == ETH_TYPE ####', in_port=in_port, match=_type) elif field.type == IP_PROTO: _proto = field.ip_proto log.info('#### field.type == IP_PROTO ####', in_port=in_port, ip_proto=ip_proto) elif field.type == IN_PORT: _port = field.port log.info('#### field.type == IN_PORT ####') elif field.type == VLAN_VID: _vlan_vid = field.vlan_vid & 0xfff log.info('#### field.type == VLAN_VID ####') elif field.type == VLAN_PCP: _vlan_pcp = field.vlan_pcp log.info('#### field.type == VLAN_PCP ####') elif field.type == UDP_DST: _udp_dst = field.udp_dst log.info('#### field.type == UDP_DST ####') elif field.type == IPV4_DST: _ipv4_dst = field.ipv4_dst log.info('#### field.type == IPV4_DST ####') a = int(hex(_ipv4_dst)[2:4], 16) b = int(hex(_ipv4_dst)[4:6], 16) c = int(hex(_ipv4_dst)[6:8], 16) d = int(hex(_ipv4_dst)[8:], 16) dn_req = EOAMPayload( body=CablelabsOUI() / DPoEOpcode_SetRequest() / AddStaticMacAddress( mac=mcastIp2McastMac('%d.%d.%d.%d' % (a, b, c, d)))) # send message log.info('ONU-send-proxied-message') self.adapter_agent.send_proxied_message( device.proxy_address, dn_req) else: raise NotImplementedError('field.type={}'.format( field.type)) for action in get_actions(flow): if action.type == OUTPUT: log.info('#### action.type == OUTPUT ####') elif action.type == POP_VLAN: log.info('#### action.type == POP_VLAN ####') elif action.type == PUSH_VLAN: log.info('#### action.type == PUSH_VLAN ####') if action.push.ethertype != 0x8100: log.error('unhandled-ether-type', ethertype=action.push.ethertype) elif action.type == SET_FIELD: log.info('#### action.type == SET_FIELD ####') assert (action.set_field.field.oxm_class == ofp.OFPXMC_OPENFLOW_BASIC) field = action.set_field.field.ofb_field if field.type == VLAN_VID: pass else: log.error('unsupported-action-set-field-type', field_type=field.type) else: log.error('UNSUPPORTED-ACTION-TYPE', action_type=action.type) else: raise Exception('Port should be 1 or 2 by our convention') def update_flows_incrementally(self, device, flow_changes, group_changes): raise NotImplementedError() def send_proxied_message(self, proxy_address, msg): raise NotImplementedError() def receive_proxied_message(self, proxy_address, msg): log.info('receive-proxied-message', proxy_address=proxy_address, msg=msg.show(dump=True)) self.incoming_messages.put(msg) @inlineCallbacks def _message_exchange(self, device): # register for receiving async messages self.adapter_agent.register_for_proxied_messages(device.proxy_address) # reset incoming message queue while self.incoming_messages.pending: _ = yield self.incoming_messages.get() # construct message msg = EOAMPayload(body=CablelabsOUI() / DPoEOpcode_GetRequest() / DeviceId()) # send message log.info('ONU-send-proxied-message') self.adapter_agent.send_proxied_message(device.proxy_address, msg) # wait till we detect incoming message yield self.incoming_messages.get() # construct install of igmp query address msg = EOAMPayload(body=CablelabsOUI() / DPoEOpcode_SetRequest() / AddStaticMacAddress(mac='01:00:5e:00:00:01')) # send message log.info('ONU-send-proxied-message') self.adapter_agent.send_proxied_message(device.proxy_address, msg) # wait till we detect incoming message yield self.incoming_messages.get() # by returning we allow the device to be shown as active, which # indirectly verified that message passing works def receive_packet_out(self, logical_device_id, egress_port_no, msg): log.info('packet-out', logical_device_id=logical_device_id, egress_port_no=egress_port_no, msg_len=len(msg)) def start_kpi_collection(self, device_id): """TMP Simulate periodic KPI metric collection from the device""" import random @inlineCallbacks # pretend that we need to do async calls def _collect(device_id, prefix): try: # Step 1: gather metrics from device (pretend it here) - examples uni_port_metrics = yield dict( tx_pkts=random.randint(0, 100), rx_pkts=random.randint(0, 100), tx_bytes=random.randint(0, 100000), rx_bytes=random.randint(0, 100000), ) pon_port_metrics = yield dict( tx_pkts=uni_port_metrics['rx_pkts'], rx_pkts=uni_port_metrics['tx_pkts'], tx_bytes=uni_port_metrics['rx_bytes'], rx_bytes=uni_port_metrics['tx_bytes'], ) onu_metrics = yield dict(cpu_util=20 + 5 * random.random(), buffer_util=10 + 10 * random.random()) # 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={ # OLT-level prefix: MetricValuePairs(metrics=onu_metrics), # OLT NNI port prefix + '.nni': MetricValuePairs(metrics=uni_port_metrics), # OLT PON port prefix + '.pon': MetricValuePairs(metrics=pon_port_metrics) }) # Step 3: submit self.adapter_agent.submit_kpis(kpi_event) except Exception as e: log.exception('failed-to-submit-kpis', e=e) prefix = 'voltha.{}.{}'.format(self.name, device_id) lc = LoopingCall(_collect, device_id, prefix) lc.start(interval=15) # TODO make this configurable
class AdtranOltAdapter(object): name = 'adtran_olt' supported_device_types = [ DeviceType( id=name, adapter=name, accepts_bulk_flow_update=True ) ] def __init__(self, adapter_agent, config): self.adapter_agent = adapter_agent self.config = config self.descriptor = Adapter( id=self.name, vendor='Adtran Inc.', version='0.1', config=AdapterConfig(log_level=LogLevel.INFO) ) log.debug('adtran_olt.__init__', adapter_agent=adapter_agent) self.devices_handlers = dict() # device_id -> AdtranOltHandler() self.interface = registry('main').get_args().interface # self.logical_device_id_to_root_device_id = dict() def start(self): """ Called once after adapter instance is loaded. Can be used to async initialization. :return: (None or Deferred) """ log.debug('starting', interface=self.interface) log.info('started', interface=self.interface) def stop(self): """ Called once before adapter is unloaded. It can be used to perform any cleanup after the adapter. :return: (None or Deferred) """ log.debug('stopping', interface=self.interface) log.info('stopped', interface=self.interface) def adapter_descriptor(self): """ Return the adapter descriptor object for this adapter. :return: voltha.Adapter grpc object (see voltha/protos/adapter.proto), with adapter-specific information and config extensions. """ log.debug('get descriptor', interface=self.interface) return self.descriptor def device_types(self): """ Return list of device types supported by the adapter. :return: voltha.DeviceTypes protobuf object, with optional type specific extensions. """ log.debug('get device_types', interface=self.interface, items=self.supported_device_types) return DeviceTypes(items=self.supported_device_types) def health(self): """ Return a 3-state health status using the voltha.HealthStatus message. :return: Deferred or direct return with voltha.HealthStatus message """ log.debug('get health', interface=self.interface) return HealthStatus(state=HealthStatus.HealthState.HEALTHY) def change_master_state(self, master): """ Called to indicate if plugin shall assume or lose master role. The master role can be used to perform functions that must be performed from a single point in the cluster. In single-node deployments of Voltha, the plugins are always in master role. :param master: (bool) True to indicate the mastership needs to be assumed; False to indicate that mastership needs to be abandoned. :return: (Deferred) which is fired by the adapter when mastership is assumed/dropped, respectively. """ log.debug('change_master_state', interface=self.interface, master=master) raise NotImplementedError() def adopt_device(self, device): """ Make sure the adapter looks after given device. Called when a device is provisioned top-down and needs to be activated by the adapter. :param device: A voltha.Device object, with possible device-type specific extensions. Such extensions shall be described as part of the device type specification returned by device_types(). :return: (Deferred) Shall be fired to acknowledge device ownership. """ log.info('adopt-device', device=device) self.devices_handlers[device.id] = AdtranOltHandler(self, device.id) reactor.callLater(0, self.devices_handlers[device.id].activate, device) return device def abandon_device(self, device): """ Make sure the adapter no longer looks after device. This is called if device ownership is taken over by another Voltha instance. :param device: A Voltha.Device object. :return: (Deferred) Shall be fired to acknowledge abandonment. """ log.info('abandon-device', device=device) handler = self.devices_handlers.pop(device.id) if handler is not None: reactor.callLater(0, handler.deactivate, device) return device def disable_device(self, device): """ This is called when a previously enabled device needs to be disabled based on a NBI call. :param device: A Voltha.Device object. :return: (Deferred) Shall be fired to acknowledge disabling the device. """ log.debug('disable_device', device=device) raise NotImplementedError() def reenable_device(self, device): """ This is called when a previously disabled device needs to be enabled based on a NBI call. :param device: A Voltha.Device object. :return: (Deferred) Shall be fired to acknowledge re-enabling the device. """ log.debug('reenable_device', device=device) raise NotImplementedError() def reboot_device(self, device): """ This is called to reboot a device based on a NBI call. The admin state of the device will not change after the reboot :param device: A Voltha.Device object. :return: (Deferred) Shall be fired to acknowledge the reboot. """ log.info('reboot_device', device=device) raise NotImplementedError() def delete_device(self, device): """ This is called to delete a device from the PON based on a NBI call. If the device is an OLT then the whole PON will be deleted. :param device: A Voltha.Device object. :return: (Deferred) Shall be fired to acknowledge the deletion. """ log.info('delete_device', device=device) raise NotImplementedError() def get_device_details(self, device): """ This is called to get additional device details based on a NBI call. :param device: A Voltha.Device object. :return: (Deferred) Shall be fired to acknowledge the retrieval of additional details. """ log.debug('get_device_details', device=device) raise NotImplementedError() def update_flows_bulk(self, device, flows, groups): """ Called after any flow table change, but only if the device supports bulk mode, which is expressed by the 'accepts_bulk_flow_update' capability attribute of the device type. :param device: A Voltha.Device object. :param flows: An openflow_v13.Flows object :param groups: An openflow_v13.Flows object :return: (Deferred or None) """ log.info('bulk-flow-update', device_id=device.id, flows=flows, groups=groups) assert len(groups.items) == 0, "Cannot yet deal with groups" raise NotImplementedError() handler = self.devices_handlers[device.id] return handler.update_flow_table(flows.items, device) def update_flows_incrementally(self, device, flow_changes, group_changes): """ [This mode is not supported yet.] :param device: A Voltha.Device object. :param flow_changes: :param group_changes: :return: """ log.debug('update_flows_incrementally', device=device, flow_changes=flow_changes, group_changes=group_changes) raise NotImplementedError() def update_pm_config(self, device, pm_configs): """ Called every time a request is made to change pm collection behavior :param device: A Voltha.Device object :param pm_configs: A Pms """ log.debug('update_pm_config', device=device, pm_configs=pm_configs) raise NotImplementedError() def send_proxied_message(self, proxy_address, msg): """ Forward a msg to a child device of device, addressed by the given proxy_address=Device.ProxyAddress(). :param proxy_address: Address info for the parent device to route the message to the child device. This was given to the child device by the parent device at the creation of the child device. :param msg: (str) The actual message to send. :return: (Deferred(None) or None) The return of this method should indicate that the message was successfully *sent*. """ log.info('send-proxied-message', proxy_address=proxy_address, msg=msg) handler = self.devices_handlers[proxy_address.device_id] handler.send_proxied_message(proxy_address, msg) def receive_proxied_message(self, proxy_address, msg): """ Pass an async message (arrived via a proxy) to this device. :param proxy_address: Address info for the parent device to route the message to the child device. This was given to the child device by the parent device at the creation of the child device. Note this is the proxy_address with which the adapter had to register prior to receiving proxied messages. :param msg: (str) The actual message received. :return: None """ log.debug('receive_proxied_message', proxy_address=proxy_address, msg=msg) raise NotImplementedError() def receive_packet_out(self, logical_device_id, egress_port_no, msg): """ Pass a packet_out message content to adapter so that it can forward it out to the device. This is only called on root devices. :param logical_device_id: :param egress_port_no: egress logical port number :param msg: actual message :return: None """ log.info('packet-out', logical_device_id=logical_device_id, egress_port_no=egress_port_no, msg_len=len(msg)) raise NotImplementedError() def receive_inter_adapter_message(self, msg): """ Called when the adapter recieves a message that was sent to it directly from another adapter. An adapter may register for these messages by calling the register_for_inter_adapter_messages() method in the adapter agent. Note that it is the responsibility of the sending and receiving adapters to properly encode and decode the message. :param msg: The message contents. :return: None """ log.info('rx_inter_adapter_msg') raise NotImplementedError() def suppress_alarm(self, filter): log.info('suppress_alarm', filter=filter) raise NotImplementedError() def unsuppress_alarm(self, filter): log.info('unsuppress_alarm', filter=filter) raise NotImplementedError()
class CigOpenomciOnuAdapter(BrcmOpenomciOnuAdapter): name = 'cig_openomci_onu' supported_device_types = [ DeviceType( id=name, #vendor_ids=[], vendor_ids=['CIGG'], adapter=name, accepts_bulk_flow_update=True) ] def __init__(self, adapter_agent, config): super(CigOpenomciOnuAdapter, self).__init__(adapter_agent, config) # self.adapter_agent = adapter_agent # self.config = config self.descriptor = Adapter( id=self.name, vendor='CIG Tech', version='0.10', config=AdapterConfig(log_level=LogLevel.INFO)) # self.devices_handlers = dict() # Customize OpenOMCI for CIG ONUs self._omci_support_cls = deepcopy(OpenOmciAgentDefaults) # self.broadcom_omci['mib-synchronizer']['state-machine'] = BrcmMibSynchronizer # self.broadcom_omci['mib-synchronizer']['database'] = MibDbVolatileDict # self.broadcom_omci['omci-capabilities']['tasks']['get-capabilities'] = BrcmCapabilitiesTask # self._omci_agent = OpenOMCIAgent(self.adapter_agent.core, # support_classes=self.broadcom_omci) self._omci_agent = OpenOMCIAgent( self.adapter_agent.core, support_classes=self._omci_support_cls) # register for adapter messages # self.adapter_agent.register_for_inter_adapter_messages() def device_types(self): return DeviceTypes(items=self.supported_device_types) def adapter_descriptor(self): return self.descriptor def device_types(self): return DeviceTypes(items=self.supported_device_types) def __download_image_success(self, image_download): log.debug("__download_image_success") def __download_image_fail(self, fail): log.debug("__download_image_fail", failure=fail) # TODO: Add callback to the defer to indicate download status def download_image(self, device, request): log.debug('download_image', device=device, request=request) onu_dev = self._omci_agent.get_device(device.id) d = onu_dev.do_onu_software_download(request) d.addCallbacks(self.__download_image_success, self.__download_image_fail) # return d def get_image_download_status(self, device, request): log.debug('get_image_download_status', device=device, request=request) onu_dev = self._omci_agent.get_device(device.id) return onu_dev.get_image_download_status( request.name) if onu_dev else None def cancel_image_download(self, device, request): log.debug('cancel_image_download', device=device, request=request) onu_dev = self._omci_agent.get_device(device.id) onu_dev.cancel_onu_software_download(request.name) def activate_image_update(self, device, request): log.debug('activate_image_update', device=device, request=request) onu_dev = self._omci_agent.get_device(device.id) d = onu_dev.do_onu_image_activate(request.name) def revert_image_update(self, device, request): log.debug('revert_image_update', device=device, request=request)
class SimulatedOltAdapter(object): name = 'simulated_olt' supported_device_types = [ DeviceType(id='simulated_olt', adapter=name, accepts_bulk_flow_update=True) ] app = Klein() def __init__(self, adapter_agent, config): self.adapter_agent = adapter_agent self.config = config self.descriptor = Adapter( id=self.name, vendor='Voltha project', version='0.1', config=AdapterConfig(log_level=LogLevel.INFO)) self.control_endpoint = None def start(self): log.debug('starting') # setup a basic web server for test control self.control_endpoint = endpoints.TCP4ServerEndpoint(reactor, 18880) self.control_endpoint.listen(self.get_test_control_site()) # TODO tmp: populate some devices and logical devices # reactor.callLater(0, self._tmp_populate_stuff) log.info('started') def stop(self): log.debug('stopping') log.info('stopped') def adapter_descriptor(self): return self.descriptor def device_types(self): return DeviceTypes(items=self.supported_device_types) def health(self): return HealthStatus(state=HealthStatus.HealthState.HEALTHY) def change_master_state(self, master): raise NotImplementedError() def adopt_device(self, device): # We kick of a simulated activation scenario reactor.callLater(0.2, self._simulate_device_activation, device) return device def abandon_device(self, device): raise NotImplementedError() def deactivate_device(self, device): raise NotImplementedError() def _tmp_populate_stuff(self): """ pretend that we discovered some devices and create: - devices - device ports for each - logical device - logical device ports """ olt = Device(id='simulated_olt_1', type='simulated_olt', root=True, vendor='simulated', model='n/a', hardware_version='n/a', firmware_version='n/a', software_version='1.0', serial_number=uuid4().hex, adapter=self.name, oper_status=OperStatus.DISCOVERED) self.adapter_agent.add_device(olt) self.adapter_agent.add_port( olt.id, Port(port_no=1, label='pon', type=Port.PON_OLT)) self.adapter_agent.add_port( olt.id, Port(port_no=2, label='eth', type=Port.ETHERNET_NNI)) onu1 = Device(id='simulated_onu_1', type='simulated_onu', root=False, parent_id=olt.id, parent_port_no=1, vendor='simulated', model='n/a', hardware_version='n/a', firmware_version='n/a', software_version='1.0', serial_number=uuid4().hex, adapter='simulated_onu', oper_status=OperStatus.DISCOVERED, vlan=101) self.adapter_agent.add_device(onu1) self.adapter_agent.add_port( onu1.id, Port(port_no=2, label='eth', type=Port.ETHERNET_UNI)) self.adapter_agent.add_port( onu1.id, Port(port_no=1, label='pon', type=Port.PON_ONU, peers=[Port.PeerPort(device_id=olt.id, port_no=1)])) onu2 = Device(id='simulated_onu_2', type='simulated_onu', root=False, parent_id=olt.id, parent_port_no=1, vendor='simulated', model='n/a', hardware_version='n/a', firmware_version='n/a', software_version='1.0', serial_number=uuid4().hex, adapter='simulated_onu', oper_status=OperStatus.DISCOVERED, vlan=102) self.adapter_agent.add_device(onu2) self.adapter_agent.add_port( onu2.id, Port(port_no=2, label='eth', type=Port.ETHERNET_UNI)) self.adapter_agent.add_port( onu2.id, Port(port_no=1, label='pon', type=Port.PON_ONU, peers=[Port.PeerPort(device_id=olt.id, port_no=1)])) ld = LogicalDevice( id='simulated1', datapath_id=1, desc=ofp_desc(mfr_desc='cord project', hw_desc='simualted pon', sw_desc='simualted pon', serial_num=uuid4().hex, dp_desc='n/a'), switch_features=ofp_switch_features( n_buffers=256, # TODO fake for now n_tables=2, # TODO ditto capabilities=( # TODO and ditto OFPC_FLOW_STATS | OFPC_TABLE_STATS | OFPC_PORT_STATS | OFPC_GROUP_STATS)), root_device_id=olt.id) self.adapter_agent.create_logical_device(ld) cap = OFPPF_1GB_FD | OFPPF_FIBER for port_no, name, device_id, device_port_no, root_port in [ (1, 'onu1', onu1.id, 2, False), (2, 'onu2', onu2.id, 2, False), (129, 'olt1', olt.id, 2, True) ]: port = LogicalPort(id=name, ofp_port=ofp_port( port_no=port_no, hw_addr=mac_str_to_tuple( '00:00:00:00:00:%02x' % port_no), name=name, config=0, state=OFPPS_LIVE, curr=cap, advertised=cap, peer=cap, curr_speed=OFPPF_1GB_FD, max_speed=OFPPF_1GB_FD), device_id=device_id, device_port_no=device_port_no, root_port=root_port) self.adapter_agent.add_logical_port(ld.id, port) olt.parent_id = ld.id self.adapter_agent.update_device(olt) @inlineCallbacks def _simulate_device_activation(self, device): # first we pretend that we were able to contact the device and obtain # additional information about it device.root = True device.vendor = 'simulated' device.model = 'n/a' device.hardware_version = 'n/a' device.firmware_version = 'n/a' device.software_version = '1.0' device.serial_number = uuid4().hex device.connect_status = ConnectStatus.REACHABLE self.adapter_agent.update_device(device) # then shortly after we create some ports for the device yield asleep(0.05) nni_port = Port(port_no=2, label='NNI facing Ethernet port', type=Port.ETHERNET_NNI, admin_state=AdminState.ENABLED, oper_status=OperStatus.ACTIVE) self.adapter_agent.add_port(device.id, nni_port) self.adapter_agent.add_port( device.id, Port(port_no=1, label='PON port', type=Port.PON_OLT, admin_state=AdminState.ENABLED, oper_status=OperStatus.ACTIVE)) # then shortly after we create the logical device with one port # that will correspond to the NNI port yield asleep(0.05) logical_device_id = uuid4().hex[:12] ld = LogicalDevice( # not setting id and datapth_id will let the adapter agent pick id desc=ofp_desc(mfr_desc='cord porject', hw_desc='simualted pon', sw_desc='simualted pon', serial_num=uuid4().hex, dp_desc='n/a'), switch_features=ofp_switch_features( n_buffers=256, # TODO fake for now n_tables=2, # TODO ditto capabilities=( # TODO and ditto OFPC_FLOW_STATS | OFPC_TABLE_STATS | OFPC_PORT_STATS | OFPC_GROUP_STATS)), root_device_id=device.id) ld_initialized = self.adapter_agent.create_logical_device(ld) cap = OFPPF_1GB_FD | OFPPF_FIBER self.adapter_agent.add_logical_port( ld_initialized.id, LogicalPort(id='nni', ofp_port=ofp_port(port_no=129, hw_addr=mac_str_to_tuple( '00:00:00:00:00:%02x' % 129), name='nni', config=0, state=OFPPS_LIVE, curr=cap, advertised=cap, peer=cap, curr_speed=OFPPF_1GB_FD, max_speed=OFPPF_1GB_FD), device_id=device.id, device_port_no=nni_port.port_no, root_port=True)) # and finally update to active device = self.adapter_agent.get_device(device.id) device.parent_id = ld_initialized.id device.oper_status = OperStatus.ACTIVE self.adapter_agent.update_device(device) # reactor.callLater(0.1, self._simulate_detection_of_onus, device.id) self.start_kpi_collection(device.id) @inlineCallbacks def _simulate_detection_of_onus(self, device_id): for i in xrange(1, 5): log.info('activate-olt-for-onu-{}'.format(i)) vlan_id = self._olt_side_onu_activation(i) yield asleep(0.05) self.adapter_agent.child_device_detected( parent_device_id=device_id, parent_port_no=1, child_device_type='simulated_onu', proxy_address=Device.ProxyAddress(device_id=device_id, channel_id=vlan_id), vlan=vlan_id) def _olt_side_onu_activation(self, seq): """ This is where if this was a real OLT, the OLT-side activation for the new ONU should be performed. By the time we return, the OLT shall be able to provide tunneled (proxy) communication to the given ONU, using the returned information. """ vlan_id = seq + 100 return vlan_id def update_flows_bulk(self, device, flows, groups): log.debug('bulk-flow-update', device_id=device.id, flows=flows, groups=groups) # sample code that analyzes the incoming flow table assert len(groups.items) == 0, "Cannot yet deal with groups" for flow in flows.items: in_port = get_in_port(flow) assert in_port is not None if in_port == 2: # Downstream rule for field in get_ofb_fields(flow): if field.type == ETH_TYPE: _type = field.eth_type pass # construct ether type based condition here elif field.type == IP_PROTO: _proto = field.ip_proto pass # construct ip_proto based condition here elif field.type == IN_PORT: _port = field.port pass # construct in_port based condition here elif field.type == VLAN_VID: _vlan_vid = field.vlan_vid pass # construct VLAN ID based filter condition here elif field.type == VLAN_PCP: _vlan_pcp = field.vlan_pcp pass # construct VLAN PCP based filter condition here elif field.type == METADATA: pass # safe to ignore # TODO else: raise NotImplementedError('field.type={}'.format( field.type)) for action in get_actions(flow): if action.type == OUTPUT: pass # construct packet emit rule here elif action.type == PUSH_VLAN: if action.push.ethertype != 0x8100: log.error('unhandled-ether-type', ethertype=action.push.ethertype) pass # construct vlan push command here elif action.type == POP_VLAN: pass # construct vlan pop command here elif action.type == SET_FIELD: assert (action.set_field.field.oxm_class == ofp.OFPXMC_OPENFLOW_BASIC) field = action.set_field.field.ofb_field if field.type == VLAN_VID: pass # construct vlan_id set command here else: log.error('unsupported-action-set-field-type', field_type=field.type) else: log.error('unsupported-action-type', action_type=action.type) # final assembly of low level device flow rule and pushing it # down to device pass elif in_port == 1: # Upstream rule for field in get_ofb_fields(flow): if field.type == ETH_TYPE: _type = field.eth_type pass # construct ether type based condition here elif field.type == IP_PROTO: _proto = field.ip_proto pass # construct ip_proto based condition here elif field.type == IN_PORT: _port = field.port pass # construct in_port based condition here elif field.type == VLAN_VID: _vlan_vid = field.vlan_vid pass # construct VLAN ID based filter condition here elif field.type == VLAN_PCP: _vlan_pcp = field.vlan_pcp pass # construct VLAN PCP based filter condition here elif field.type == UDP_SRC: _udp_src = field.udp_src pass # construct UDP SRC based filter here elif field.type == UDP_DST: _udp_dst = field.udp_dst pass # construct UDP DST based filter here # TODO else: raise NotImplementedError('field.type={}'.format( field.type)) for action in get_actions(flow): if action.type == OUTPUT: pass # construct packet emit rule here elif action.type == PUSH_VLAN: if action.push.ethertype != 0x8100: log.error('unhandled-ether-type', ethertype=action.push.ethertype) pass # construct vlan push command here elif action.type == SET_FIELD: assert (action.set_field.field.oxm_class == ofp.OFPXMC_OPENFLOW_BASIC) field = action.set_field.field.ofb_field if field.type == VLAN_VID: pass # construct vlan_id set command here else: log.error('unsupported-action-set-field-type', field_type=field.type) else: log.error('unsupported-action-type', action_type=action.type) # final assembly of low level device flow rule and pushing it # down to device pass else: raise Exception('Port should be 1 or 2 by our convention') def update_flows_incrementally(self, device, flow_changes, group_changes): raise NotImplementedError() def send_proxied_message(self, proxy_address, msg): log.info('send-proxied-message', proxy_address=proxy_address, msg=msg) # we mimic a response by sending the same message back in a short time reactor.callLater(0.2, self.adapter_agent.receive_proxied_message, proxy_address, msg) def receive_proxied_message(self, proxy_address, msg): raise NotImplementedError() def receive_packet_out(self, logical_device_id, egress_port_no, msg): log.info('packet-out', logical_device_id=logical_device_id, egress_port_no=egress_port_no, msg_len=len(msg)) def start_kpi_collection(self, device_id): """Simulate periodic KPI metric collection from the device""" import random @inlineCallbacks # pretend that we need to do async calls def _collect(device_id, prefix): try: # Step 1: gather metrics from device (pretend it here) - examples nni_port_metrics = yield dict( tx_pkts=random.randint(0, 100), rx_pkts=random.randint(0, 100), tx_bytes=random.randint(0, 100000), rx_bytes=random.randint(0, 100000), ) pon_port_metrics = yield dict( tx_pkts=nni_port_metrics['rx_pkts'], rx_pkts=nni_port_metrics['tx_pkts'], tx_bytes=nni_port_metrics['rx_bytes'], rx_bytes=nni_port_metrics['tx_bytes'], ) olt_metrics = yield dict(cpu_util=20 + 5 * random.random(), buffer_util=10 + 10 * random.random()) # 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={ # OLT-level prefix: MetricValuePairs(metrics=olt_metrics), # OLT NNI port prefix + '.nni': MetricValuePairs(metrics=nni_port_metrics), # OLT PON port prefix + '.pon': MetricValuePairs(metrics=pon_port_metrics) }) # Step 3: submit self.adapter_agent.submit_kpis(kpi_event) except Exception as e: log.exception('failed-to-submit-kpis', e=e) prefix = 'voltha.{}.{}'.format(self.name, device_id) lc = LoopingCall(_collect, device_id, prefix) lc.start(interval=15) # TODO make this configurable # ~~~~~~~~~~~~~~~~~~~~ Embedded test Klein rest server ~~~~~~~~~~~~~~~~~~~~ def get_test_control_site(self): return Site(self.app.resource()) @app.route('/devices/<string:device_id>/detect_onus') def detect_onus(self, request, device_id): log.info('detect-onus', request=request, device_id=device_id) self._simulate_detection_of_onus(device_id) return '{"status": "OK"}' @app.route('/devices/<string:device_id>/test_eapol_in') def test_eapol_in(self, request, device_id): """Simulate a packet in message posted upstream""" log.info('test_eapol_in', request=request, device_id=device_id) eapol_start = str( Ether(src='00:11:22:33:44:55') / EAPOL(type=1, len=0) / Padding(load=42 * '\x00')) device = self.adapter_agent.get_device(device_id) self.adapter_agent.send_packet_in(logical_device_id=device.parent_id, logical_port_no=1, packet=eapol_start) return '{"status": "sent"}'
class TibitOnuAdapter(object): name = 'tibit_onu' supported_device_types = [ DeviceType( id='tibit_onu', adapter=name, accepts_bulk_flow_update=True ) ] def __init__(self, adapter_agent, config): self.adapter_agent = adapter_agent self.config = config self.descriptor = Adapter( id=self.name, vendor='Tibit Communications Inc.', version='0.1', config=AdapterConfig(log_level=LogLevel.INFO) ) self.incoming_messages = DeferredQueue() def start(self): log.debug('starting') log.info('started') def stop(self): log.debug('stopping') log.info('stopped') def adapter_descriptor(self): return self.descriptor def device_types(self): return DeviceTypes(items=self.supported_device_types) def health(self): return HealthStatus(state=HealthStatus.HealthState.HEALTHY) def change_master_state(self, master): raise NotImplementedError() def update_pm_config(self, device, pm_configs): raise NotImplementedError() def adopt_device(self, device): log.info('adopt-device', device=device) reactor.callLater(0.1, self._onu_device_activation, device) return device @inlineCallbacks def _onu_device_activation(self, device): # 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 # TODO: For now, pretend that we were able to contact the device and obtain # additional information about it. Should add real message. device.vendor = 'Tibit Communications, Inc.' device.model = '10G GPON ONU' device.hardware_version = 'fa161020' device.firmware_version = '16.12.02' device.software_version = '1.0' device.serial_number = uuid4().hex device.connect_status = ConnectStatus.REACHABLE self.adapter_agent.update_device(device) # then shortly after we create some ports for the device uni_port = Port( port_no=2, label='UNI facing Ethernet port', type=Port.ETHERNET_UNI, admin_state=AdminState.ENABLED, oper_status=OperStatus.ACTIVE ) self.adapter_agent.add_port(device.id, uni_port) self.adapter_agent.add_port(device.id, Port( port_no=1, label='PON port', type=Port.PON_ONU, admin_state=AdminState.ENABLED, oper_status=OperStatus.ACTIVE, peers=[ Port.PeerPort( device_id=device.parent_id, port_no=device.parent_port_no ) ] )) # TODO adding vports to the logical device shall be done by agent? # then we create the logical device port that corresponds to the UNI # port of the device # obtain logical device id parent_device = self.adapter_agent.get_device(device.parent_id) logical_device_id = parent_device.parent_id assert logical_device_id # we are going to use the proxy_address.channel_id as unique number # and name for the virtual ports, as this is guaranteed to be unique # in the context of the OLT port, so it is also unique in the context # of the logical device port_no = device.proxy_address.channel_id cap = OFPPF_10GB_FD | OFPPF_FIBER self.adapter_agent.add_logical_port(logical_device_id, LogicalPort( id=str(port_no), ofp_port=ofp_port( port_no=port_no, hw_addr=mac_str_to_tuple(device.mac_address), name='uni-{}'.format(port_no), config=0, state=OFPPS_LIVE, curr=cap, advertised=cap, peer=cap, curr_speed=OFPPF_10GB_FD, max_speed=OFPPF_10GB_FD ), device_id=device.id, device_port_no=uni_port.port_no )) # simulate a proxied message sending and receving a reply reply = yield self._message_exchange(device) # TODO - Need to add validation of reply and decide what to do upon failure # and finally update to "ACTIVE" device = self.adapter_agent.get_device(device.id) device.oper_status = OperStatus.ACTIVE self.adapter_agent.update_device(device) # TODO - Disable Stats Reporting for the moment #self.start_kpi_collection(device.id) def abandon_device(self, device): raise NotImplementedError(0 ) def disable_device(self, device): raise NotImplementedError() def reenable_device(self, device): raise NotImplementedError() def reboot_device(self, device): raise NotImplementedError() def delete_device(self, device): raise NotImplementedError() def get_device_details(self, device): raise NotImplementedError() @inlineCallbacks def update_flows_bulk(self, device, flows, groups): log.info('########################################') log.info('bulk-flow-update', device_id=device.id, flows=flows, groups=groups) assert len(groups.items) == 0, "Cannot yet deal with groups" Clause = {v: k for k, v in ClauseSubtypeEnum.iteritems()} Operator = {v: k for k, v in RuleOperatorEnum.iteritems()} for flow in flows.items: in_port = get_in_port(flow) assert in_port is not None precedence = 255 - min(flow.priority / 256, 255) if in_port == 2: log.info('#### Upstream Rule ####') up_req = ( EOAMPayload() / EOAM_VendSpecificMsg(oui=CableLabs_OUI) / EOAM_DpoeMsg(dpoe_opcode=0x03) ) #TODO - There is no body to the message above, is there ever an Upstream Rule for field in get_ofb_fields(flow): if field.type == ETH_TYPE: _type = field.eth_type log.info('#### field.type == ETH_TYPE ####',field_type=_type) elif field.type == IP_PROTO: _proto = field.ip_proto log.info('#### field.type == IP_PROTO ####') elif field.type == IN_PORT: _port = field.port log.info('#### field.type == IN_PORT ####', port=_port) elif field.type == VLAN_VID: _vlan_vid = field.vlan_vid & 0xfff log.info('#### field.type == VLAN_VID ####', vlan=_vlan_vid) elif field.type == VLAN_PCP: _vlan_pcp = field.vlan_pcp log.info('#### field.type == VLAN_PCP ####', pcp=_vlan_pcp) elif field.type == UDP_DST: _udp_dst = field.udp_dst log.info('#### field.type == UDP_DST ####') elif field.type == IPV4_DST: _ipv4_dst = field.ipv4_dst log.info('#### field.type == IPV4_DST ####') else: log.info('#### field.type == NOT IMPLEMENTED!! ####') raise NotImplementedError('field.type={}'.format( field.type)) for action in get_actions(flow): if action.type == OUTPUT: log.info('#### action.type == OUTPUT ####') elif action.type == POP_VLAN: log.info('#### action.type == POP_VLAN ####') elif action.type == PUSH_VLAN: log.info('#### action.type == PUSH_VLAN ####') if action.push.ethertype != 0x8100: log.error('unhandled-tpid', ethertype=action.push.ethertype) elif action.type == SET_FIELD: log.info('#### action.type == SET_FIELD ####') assert (action.set_field.field.oxm_class == ofp.OFPXMC_OPENFLOW_BASIC) field = action.set_field.field.ofb_field if field.type == VLAN_VID: pass else: log.error('unsupported-action-set-field-type', field_type=field.type) else: log.error('UNSUPPORTED-ACTION-TYPE', action_type=action.type) elif in_port == 1: log.info('#### Downstream Rule ####') #### Loop through fields again... for field in get_ofb_fields(flow): if field.type == ETH_TYPE: _type = field.eth_type log.info('#### field.type == ETH_TYPE ####', in_port=in_port, match=_type) elif field.type == IP_PROTO: _proto = field.ip_proto log.info('#### field.type == IP_PROTO ####', in_port=in_port, ip_proto=ip_proto) elif field.type == IN_PORT: _port = field.port log.info('#### field.type == IN_PORT ####') elif field.type == VLAN_VID: _vlan_vid = field.vlan_vid & 0xfff log.info('#### field.type == VLAN_VID ####') elif field.type == VLAN_PCP: _vlan_pcp = field.vlan_pcp log.info('#### field.type == VLAN_PCP ####') elif field.type == UDP_DST: _udp_dst = field.udp_dst log.info('#### field.type == UDP_DST ####') elif field.type == IPV4_DST: _ipv4_dst = field.ipv4_dst log.info('#### field.type == IPV4_DST ####') a = int(hex(_ipv4_dst)[2:4], 16) b = int(hex(_ipv4_dst)[4:6], 16) c = int(hex(_ipv4_dst)[6:8], 16) d = int(hex(_ipv4_dst)[8:], 16) dn_req = ( EOAMPayload() / EOAM_VendSpecificMsg(oui=CableLabs_OUI) / EOAM_DpoeMsg(dpoe_opcode=0x03, body=AddStaticMacAddress(mac=mcastIp2McastMac('%d.%d.%d.%d' % (a,b,c,d))) )) # send message log.info('ONU-send-proxied-message to Set Static IP MCAST address for ONU: {}'.format(device.mac_address)) self.adapter_agent.send_proxied_message(device.proxy_address, dn_req) # Get and process the Set Response ack = False start_time = time.time() # Loop until we have a set response or timeout while not ack: frame = yield self.incoming_messages.get() #TODO - Need to add propoer timeout functionality #if (time.time() - start_time) > TIBIT_MSG_WAIT_TIME or (frame is None): # break # don't wait forever respType = self._get_oam_msg_type(frame) log.info('Received OAM Message 0x %s' % str(respType)) #Check that the message received is a Set Response if (respType == RxedOamMsgTypeEnum["DPoE Set Response"]): ack = True else: # Handle unexpected events/OMCI messages self._check_resp(frame) # Verify Set Response if ack: log.info('ONU-response received for Set Static IP MCAST address for ONU: {}'.format(device.mac_address)) (rc,branch,leaf,status) = self._check_set_resp(frame) if (rc == True): log.info('Set Response had no errors') else: log.info('Set Response had errors - Branch 0x{:X} Leaf 0x{:0>4X} {}'.format(branch, leaf, DPoEVariableResponseCodes[status])) else: raise NotImplementedError('field.type={}'.format( field.type)) for action in get_actions(flow): if action.type == OUTPUT: log.info('#### action.type == OUTPUT ####') elif action.type == POP_VLAN: log.info('#### action.type == POP_VLAN ####') elif action.type == PUSH_VLAN: log.info('#### action.type == PUSH_VLAN ####') if action.push.ethertype != 0x8100: log.error('unhandled-ether-type', ethertype=action.push.ethertype) elif action.type == SET_FIELD: log.info('#### action.type == SET_FIELD ####') assert (action.set_field.field.oxm_class == ofp.OFPXMC_OPENFLOW_BASIC) field = action.set_field.field.ofb_field if field.type == VLAN_VID: pass else: log.error('unsupported-action-set-field-type', field_type=field.type) else: log.error('UNSUPPORTED-ACTION-TYPE', action_type=action.type) else: raise Exception('Port should be 1 or 2 by our convention') def update_flows_incrementally(self, device, flow_changes, group_changes): raise NotImplementedError() def send_proxied_message(self, proxy_address, msg): raise NotImplementedError() def receive_proxied_message(self, proxy_address, msg): log.info('receive-proxied-message', proxy_address=proxy_address, msg=msg.show(dump=True)) self.incoming_messages.put(msg) @inlineCallbacks def _message_exchange(self, device): # register for receiving async messages self.adapter_agent.register_for_proxied_messages(device.proxy_address) # reset incoming message queue while self.incoming_messages.pending: _ = yield self.incoming_messages.get() # construct message msg = ( EOAMPayload() / EOAM_VendSpecificMsg(oui=CableLabs_OUI) / EOAM_DpoeMsg(dpoe_opcode=0x01,body=DeviceId()) ) # send message log.info('ONU-send-proxied-message to Get Device Id for ONU: {}'.format(device.mac_address)) self.adapter_agent.send_proxied_message(device.proxy_address, msg) # wait till we detect incoming message yield self.incoming_messages.get() log.info('ONU-response received for Get Device Id for ONU: {}'.format(device.mac_address)) #TODO Add a timeout to the above, if we do not recieve a message #The above get request/ get response is done to verify the message exchange is #functioning correctly, there is nothing to store from the response # construct install of igmp query address msg = ( EOAMPayload() / EOAM_VendSpecificMsg(oui=CableLabs_OUI) / EOAM_DpoeMsg(dpoe_opcode=0x03,body=AddStaticMacAddress(mac='01:00:5e:00:00:01') )) # send message log.info('ONU-send-proxied-message to Set Static IGMP MAC address for ONU: {}'.format(device.mac_address)) self.adapter_agent.send_proxied_message(device.proxy_address, msg) # Get and process the Set Response ack = False start_time = time.time() # Loop until we have a set response or timeout while not ack: frame = yield self.incoming_messages.get() #TODO - Need to add propoer timeout functionality #if (time.time() - start_time) > TIBIT_MSG_WAIT_TIME or (frame is None): # break # don't wait forever respType = self._get_oam_msg_type(frame) log.info('Received OAM Message 0x %s' % str(respType)) #Check that the message received is a Set Response if (respType == RxedOamMsgTypeEnum["DPoE Set Response"]): ack = True else: # Handle unexpected events/OMCI messages self._check_resp(frame) # Verify Set Response if ack: log.info('ONU-response received for Set Static IGMP MAC address for ONU: {}'.format(device.mac_address)) (rc,branch,leaf,status) = self._check_set_resp(frame) if (rc == True): log.info('Set Response had no errors') else: log.info('Set Response had errors - Branch 0x{:X} Leaf 0x{:0>4X} {}'.format(branch, leaf, DPoEVariableResponseCodes[status])) # construct multicast LLID set # TODO - This is needed to support multicast traffic for GPON. This should only be done for a # a GPON ONU and the UnicastLink value needs to come from the OLT. This will work only for # a single GPON ONU. msg = ( EOAMPayload() / EOAM_VendSpecificMsg(oui=CableLabs_OUI) / EOAM_DpoeMsg(dpoe_opcode=0x06,body=MulticastRegisterSet(MulticastLink=0x10bc, UnicastLink=0x1008) )) # send message log.info('ONU-send-proxied-message to Set Static IGMP MAC address for ONU: {}'.format(device.mac_address)) self.adapter_agent.send_proxied_message(device.proxy_address, msg) # The MulticastRegisterSet does not currently return a response. Just hope it worked. # by returning we allow the device to be shown as active, which # indirectly verified that message passing works def receive_packet_out(self, logical_device_id, egress_port_no, msg): log.info('packet-out', logical_device_id=logical_device_id, egress_port_no=egress_port_no, msg_len=len(msg)) def receive_inter_adapter_message(self, msg): raise NotImplementedError() def suppress_alarm(self, filter): raise NotImplementedError() def unsuppress_alarm(self, filter): raise NotImplementedError() def start_kpi_collection(self, device_id): """TMP Simulate periodic KPI metric collection from the device""" import random @inlineCallbacks # pretend that we need to do async calls def _collect(device_id, prefix): try: # Step 1: gather metrics from device (pretend it here) - examples uni_port_metrics = yield dict( tx_pkts=random.randint(0, 100), rx_pkts=random.randint(0, 100), tx_bytes=random.randint(0, 100000), rx_bytes=random.randint(0, 100000), ) pon_port_metrics = yield dict( tx_pkts=uni_port_metrics['rx_pkts'], rx_pkts=uni_port_metrics['tx_pkts'], tx_bytes=uni_port_metrics['rx_bytes'], rx_bytes=uni_port_metrics['tx_bytes'], ) onu_metrics = yield dict( cpu_util=20 + 5 * random.random(), buffer_util=10 + 10 * random.random() ) # 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={ # OLT-level prefix: MetricValuePairs(metrics=onu_metrics), # OLT NNI port prefix + '.nni': MetricValuePairs(metrics=uni_port_metrics), # OLT PON port prefix + '.pon': MetricValuePairs(metrics=pon_port_metrics) } ) # Step 3: submit self.adapter_agent.submit_kpis(kpi_event) except Exception as e: log.exception('failed-to-submit-kpis', e=e) prefix = 'voltha.{}.{}'.format(self.name, device_id) lc = LoopingCall(_collect, device_id, prefix) lc.start(interval=15) # TODO make this configurable # Methods for Get / Set Response Processing from eoam_messages def _get_oam_msg_type(self, frame): respType = RxedOamMsgTypeEnum["Unknown"] recv_frame = frame if recv_frame.haslayer(EOAMPayload): if recv_frame.haslayer(EOAMEvent): recv_frame = RxedOamMsgTypeEnum["Event Notification"] elif recv_frame.haslayer(EOAM_OmciMsg): respType = RxedOamMsgTypeEnum["OMCI Message"] else: dpoeOpcode = 0x00 if recv_frame.haslayer(EOAM_TibitMsg): dpoeOpcode = recv_frame.getlayer(EOAM_TibitMsg).dpoe_opcode; elif recv_frame.haslayer(EOAM_DpoeMsg): dpoeOpcode = recv_frame.getlayer(EOAM_DpoeMsg).dpoe_opcode; # Get Response if (dpoeOpcode == 0x02): respType = RxedOamMsgTypeEnum["DPoE Get Response"] # Set Response elif (dpoeOpcode == 0x04): respType = RxedOamMsgTypeEnum["DPoE Set Response"] # File Transfer ACK elif (dpoeOpcode == 0x09): respType = RxedOamMsgTypeEnum["DPoE File Transfer"] else: log.info('Unsupported DPoE Opcode {:0>2X}'.format(dpoeOpcode)) else: log.info('Invalid OAM Header') return respType def _get_value_from_msg(self, frame, branch, leaf): retVal = False value = 0 recv_frame = frame if recv_frame.haslayer(EOAMPayload): payload = recv_frame.payload if hasattr(payload, 'body'): loadstr = payload.body.load # Get a specific TLV value (retVal,bytesRead,value,retbranch,retleaf) = self._handle_get_value(loadstr, 0, branch, leaf) else: log.info('received frame has no payload') else: log.info('Invalid OAM Header') return retVal,value, def _handle_get_value(self, loadstr, startOfTlvs, queryBranch, queryLeaf): retVal = False; value = 0 branch = 0 leaf = 0 bytesRead = startOfTlvs loadstrlen = len(loadstr) while (bytesRead <= loadstrlen): (branch, leaf) = struct.unpack_from('>BH', loadstr, bytesRead) if (branch != 0): bytesRead += 3 length = struct.unpack_from('>B', loadstr, bytesRead)[0] bytesRead += 1 if (length == 1): value = struct.unpack_from(">B", loadstr, bytesRead)[0] elif (length == 2): value = struct.unpack_from(">H", loadstr, bytesRead)[0] elif (length == 4): value = struct.unpack_from(">I", loadstr, bytesRead)[0] elif (length == 8): value = struct.unpack_from(">Q", loadstr, bytesRead)[0] else: if (length >= 0x80): log.info('Branch 0x{:0>2X} Leaf 0x{:0>4X} {}'.format(branch, leaf, DPoEVariableResponseCodes[length])) # Set length to zero so bytesRead doesn't get mistakenly incremented below length = 0 else: # Attributes with a length of zero are actually 128 bytes long if (length == 0): length = 128; valStr = ">{}s".format(length) value = struct.unpack_from(valStr, loadstr, bytesRead)[0] if (length > 0): bytesRead += length if (branch != 0xD6): if ( ((queryBranch == 0) and (queryLeaf == 0)) or ((queryBranch == branch) and (queryLeaf == leaf)) ): # Prevent zero-lengthed values from returning success if (length > 0): retVal = True; break else: break if (retVal == False): value = 0 return retVal,bytesRead,value,branch,leaf def _check_set_resp(self, frame): rc = False branch = 0 leaf = 0 status = 0 recv_frame = frame if recv_frame.haslayer(EOAMPayload): payload = recv_frame.payload if hasattr(payload, 'body'): loadstr = payload.body.load # Get a specific TLV value (rc,branch,leaf,status) = self._check_set_resp_attrs(loadstr, 0) else: log.info('received frame has no payload') else: log.info('Invalid OAM Header') return rc,branch,leaf,status def _check_resp(self, frame): respType = RxedOamMsgTypeEnum["Unknown"] recv_frame = frame if recv_frame.haslayer(EOAMPayload): if recv_frame.haslayer(EOAMEvent): self.handle_oam_event(recv_frame) elif recv_frame.haslayer(EOAM_OmciMsg): self.handle_omci(recv_frame) else: dpoeOpcode = 0x00 if recv_frame.haslayer(EOAM_TibitMsg): dpoeOpcode = recv_frame.getlayer(EOAM_TibitMsg).dpoe_opcode; elif recv_frame.haslayer(EOAM_DpoeMsg): dpoeOpcode = recv_frame.getlayer(EOAM_DpoeMsg).dpoe_opcode; if hasattr(recv_frame, 'body'): payload = recv_frame.payload loadstr = payload.body.load # Get Response if (dpoeOpcode == 0x02): # Get a specific TLV value branch = 0xD7 leaf = 0x0006 (rc,bytesRead,value,retbranch,retleaf) = self._handle_get_value(loadstr, startOfTlvs, branch, leaf) if (rc == True): log.info('Branch 0x{:X} Leaf 0x{:0>4X} value = {}'.format(branch, leaf, value)) else: log.info('Branch 0x{:X} Leaf 0x{:0>4X} no value'.format(branch, leaf)) # Walk through all TLV values bytesRead = 0 rc = True while(rc == True): branch = 0 leaf = 0 (rc,bytesRead,value,branch,leaf) = self._handle_get_value(loadstr, bytesRead, branch, leaf) if (rc == True): log.info('Branch 0x{:0>2X} Leaf 0x{:0>4X} value = {}'.format(branch, leaf, value)) elif (branch != 0): log.info('Branch 0x{:0>2X} Leaf 0x{:0>4X} no value'.format(branch, leaf)) # Set Response elif (dpoeOpcode == 0x04): (rc,branch,leaf,status) = self._check_set_resp_attrs(loadstr, 0) if (rc == True): log.info('Set Response had no errors') else: log.info('Branch 0x{:X} Leaf 0x{:0>4X} {}'.format(branch, leaf, DPoEVariableResponseCodes[status])) # File Transfer ACK elif (dpoeOpcode == 0x09): rc = self._handle_fx_ack(loadstr, bytesRead, block_number) else: log.info('Unsupported DPoE Opcode {:0>2X}'.format(dpoeOpcode)) else: log.info('Invalid OAM Header') return respType def _check_set_resp_attrs(self, loadstr, startOfTlvs): retVal = True; branch = 0 leaf = 0 length = 0 bytesRead = startOfTlvs loadstrlen = len(loadstr) while (bytesRead <= loadstrlen): (branch, leaf) = struct.unpack_from('>BH', loadstr, bytesRead) # print "Branch/Leaf 0x{:0>2X}/0x{:0>4X}".format(branch, leaf) if (branch != 0): bytesRead += 3 length = struct.unpack_from('>B', loadstr, bytesRead)[0] # print "Length: 0x{:0>2X} ({})".format(length,length) bytesRead += 1 if (length >= 0x80): log.info('Branch 0x{:0>2X} Leaf 0x{:0>4X} {}'.format(branch, leaf, DPoEVariableResponseCodes[length])) if (length > 0x80): retVal = False; break; else: bytesRead += length else: break return retVal,branch,leaf,length def _handle_fx_ack(self, loadstr, startOfXfer, block_number): retVal = False (fx_opcode, acked_block, response_code) = struct.unpack_from('>BHB', loadstr, startOfXfer) #print "fx_opcode: 0x%x" % fx_opcode #print "acked_block: 0x%x" % acked_block #print "response_code: 0x%x" % response_code if (fx_opcode != 0x03): log.info('unexpected fx_opcode 0x%x (expected 0x03)' % fx_opcode) elif (acked_block != block_number): log.info('unexpected acked_block 0x%x (expected 0x%x)' % (acked_block, block_number)) elif (response_code != 0): log.info('unexpected response_code 0x%x (expected 0x00)' % response_code) else: retVal = True;
class MapleOltAdapter(object): name = 'maple_olt' supported_device_types = [ DeviceType(id=name, adapter=name, accepts_bulk_flow_update=True) ] def __init__(self, adapter_agent, config): self.adapter_agent = adapter_agent self.config = config self.descriptor = Adapter( id=self.name, vendor='Voltha project', version='0.4', config=AdapterConfig(log_level=LogLevel.INFO)) self.devices_handlers = dict() # device_id -> MapleOltHandler() self.logical_device_id_to_root_device_id = dict() def start(self): log.debug('starting') log.info('started') def stop(self): log.debug('stopping') log.info('stopped') def adapter_descriptor(self): return self.descriptor def device_types(self): return DeviceTypes(items=self.supported_device_types) def health(self): return HealthStatus(state=HealthStatus.HealthState.HEALTHY) def change_master_state(self, master): raise NotImplementedError() def adopt_device(self, device): self.devices_handlers[device.id] = MapleOltHandler(self, device.id) reactor.callLater(0, self.devices_handlers[device.id].activate, device) return device def abandon_device(self, device): raise NotImplementedError() def deactivate_device(self, device): raise NotImplementedError() def update_flows_bulk(self, device, flows, groups): log.info('bulk-flow-update', device_id=device.id, flows=flows, groups=groups) assert len(groups.items) == 0, "Cannot yet deal with groups" handler = self.devices_handlers[device.id] return handler.update_flow_table(flows.items, device) def update_flows_incrementally(self, device, flow_changes, group_changes): raise NotImplementedError() def send_proxied_message(self, proxy_address, msg): log.info('send-proxied-message', proxy_address=proxy_address, msg=msg) handler = self.devices_handlers[proxy_address.device_id] handler.send_proxied_message(proxy_address, msg) def receive_proxied_message(self, proxy_address, msg): raise NotImplementedError() def receive_packet_out(self, logical_device_id, egress_port_no, msg): def ldi_to_di(ldi): di = self.logical_device_id_to_root_device_id.get(ldi) if di is None: logical_device = self.adapter_agent.get_logical_device(ldi) di = logical_device.root_device_id self.logical_device_id_to_root_device_id[ldi] = di return di device_id = ldi_to_di(logical_device_id) handler = self.devices_handlers[device_id] handler.packet_out(egress_port_no, msg)
class RubyAdapter(object): name = "microsemi_olt" supported_device_types = [ DeviceType( id=name, adapter=name, accepts_bulk_flow_update=True ) ] def __init__(self, adaptor_agent, config): self.adaptor_agent = adaptor_agent self.config = config self.olts = {} self.descriptor = Adapter( id=self.name, vendor='Microsemi / Celestica', version='0.1', config=AdapterConfig(log_level=LogLevel.INFO) ) self.interface = registry('main').get_args().interface def start(self): log.info('starting') log.info('started') return self def stop(self): log.debug('stopping') for target in self.olts.keys(): self._abandon(target) log.info('stopped') return self def adapter_descriptor(self): return self.descriptor def device_types(self): return DeviceTypes(items=self.supported_device_types) def health(self): return HealthStatus(state=HealthStatus.HealthState.HEALTHY) def change_master_state(self, master): raise NotImplementedError() def adopt_device(self, device): device_manager = DeviceManager(device, self.adaptor_agent) target = device.mac_address comm = PAS5211Communication(dst_mac=target, iface=self.interface) olt = OltStateMachine(iface=self.interface, comm=comm, target=target, device=device_manager) activation = ActivationWatcher(iface=self.interface, comm=comm, target=target, device=device_manager) reactor.callLater(0, self._init_olt, olt, activation) log.info('adopted-device', device=device) self.olts[target] = (olt, activation, comm) def reconcile_device(self, device): raise NotImplementedError() def abandon_device(self, device): self._abandon(device.mac_address) def disable_device(self, device): raise NotImplementedError() def reenable_device(self, device): raise NotImplementedError() def update_pm_config(self, device, pm_configs): raise NotImplementedError() def reboot_device(self, device): raise NotImplementedError() def download_image(self, device, request): raise NotImplementedError() def get_image_download_status(self, device, request): raise NotImplementedError() def cancel_image_download(self, device, request): raise NotImplementedError() def activate_image_update(self, device, request): raise NotImplementedError() def revert_image_update(self, device, request): raise NotImplementedError() 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 """ log.info('self-test-device', device=device.id) raise NotImplementedError() def delete_device(self, device): raise NotImplementedError() def get_device_details(self, device): raise NotImplementedError() def update_flows_bulk(self, device, flows, groups): log.debug('bulk-flow-update', device_id=device.id, flows=flows, groups=groups) def create_interface(self, device, data): raise NotImplementedError() def update_interface(self, device, data): raise NotImplementedError() def remove_interface(self, device, data): raise NotImplementedError() def receive_onu_detect_state(self, device_id, state): raise NotImplementedError() def create_tcont(self, device, tcont_data, traffic_descriptor_data): raise NotImplementedError() def update_tcont(self, device, tcont_data, traffic_descriptor_data): raise NotImplementedError() def remove_tcont(self, device, tcont_data, traffic_descriptor_data): raise NotImplementedError() def create_gemport(self, device, data): raise NotImplementedError() def update_gemport(self, device, data): raise NotImplementedError() def remove_gemport(self, device, data): raise NotImplementedError() def create_multicast_gemport(self, device, data): raise NotImplementedError() def update_multicast_gemport(self, device, data): raise NotImplementedError() def remove_multicast_gemport(self, device, data): raise NotImplementedError() def create_multicast_distribution_set(self, device, data): raise NotImplementedError() def update_multicast_distribution_set(self, device, data): raise NotImplementedError() def remove_multicast_distribution_set(self, device, data): raise NotImplementedError() def send_proxied_message(self, proxy_address, msg): device = self.adaptor_agent.get_device(proxy_address.device_id) _, _, comm = self.olts[device.mac_address] if isinstance(msg, OmciFrame): log.info('send-omci-proxied-message', proxy_address=proxy_address, device=device) # TODO make this more efficient omci_proxy = OMCIProxy(proxy_address=proxy_address, msg=msg, adapter_agent=self.adaptor_agent, target=device.mac_address, comm=comm, iface=self.interface) omci_proxy.runbg() else: log.info('send-proxied-message', proxy_address=proxy_address) api_proxy = APIProxy(proxy_address=proxy_address, msg=msg, adapter_agent=self.adaptor_agent, target=device.mac_address, comm=comm, iface=self.interface) api_proxy.runbg() def receive_proxied_message(self, proxy_address, msg): raise NotImplementedError() def update_flows_incrementally(self, device, flow_changes, group_changes): raise NotImplementedError() def receive_packet_out(self, logical_device_id, egress_port_no, msg): log.info('packet-out', logical_device_id=logical_device_id, egress_port_no=egress_port_no, msg_len=len(msg)) def receive_inter_adapter_message(self, msg): raise NotImplementedError() def suppress_alarm(self, filter): raise NotImplementedError() def unsuppress_alarm(self, filter): raise NotImplementedError() ## # Private methods ## def _init_olt(self, olt, activation_watch): olt.runbg() activation_watch.runbg() def _abandon(self, target): olt, activation, _ = self.olts[target] olt.stop() activation.stop() del self.olts[target]
class AdtranOnuAdapter(object): name = 'adtran_onu' version = '0.1' supported_device_types = [ DeviceType( id=name, vendor_id='ADTN', adapter=name, accepts_bulk_flow_update=True ) ] def __init__(self, adapter_agent, config): self.adapter_agent = adapter_agent self.config = config self.descriptor = Adapter( id=self.name, vendor='Adtran, Inc.', version=self.version, config=AdapterConfig(log_level=LogLevel.INFO) ) self.devices_handlers = dict() # device_id -> AdtranOnuHandler() def start(self): log.debug('starting') log.info('started') def stop(self): log.debug('stopping') log.info('stopped') def adapter_descriptor(self): return self.descriptor def device_types(self): return DeviceTypes(items=self.supported_device_types) def health(self): return HealthStatus(state=HealthStatus.HealthState.HEALTHY) def change_master_state(self, master): raise NotImplementedError() def adopt_device(self, device): log.info('adopt_device', device_id=device.id) self.devices_handlers[device.proxy_address.channel_id] = AdtranOnuHandler(self, device.id) reactor.callLater(0, self.devices_handlers[device.proxy_address.channel_id].activate, device) return device def reconcile_device(self, device): raise NotImplementedError() def abandon_device(self, device): raise NotImplementedError() def disable_device(self, device): raise NotImplementedError() def reenable_device(self, device): raise NotImplementedError() def reboot_device(self, device): raise NotImplementedError() def download_image(self, device, request): raise NotImplementedError() def get_image_download_status(self, device, request): raise NotImplementedError() def cancel_image_download(self, device, request): raise NotImplementedError() def activate_image_update(self, device, request): raise NotImplementedError() def revert_image_update(self, device, request): raise NotImplementedError() def self_test_device(self, device): raise NotImplementedError() def delete_device(self, device): raise NotImplementedError() def get_device_details(self, device): raise NotImplementedError() def update_pm_config(self, device, pm_configs): raise NotImplementedError() def update_flows_bulk(self, device, flows, groups): log.info('bulk-flow-update', device_id=device.id, flows=flows, groups=groups) assert len(groups.items) == 0 handler = self.devices_handlers[device.proxy_address.channel_id] return handler.update_flow_table(device, flows.items) def update_flows_incrementally(self, device, flow_changes, group_changes): raise NotImplementedError() def send_proxied_message(self, proxy_address, msg): log.info('send-proxied-message', proxy_address=proxy_address, msg=msg) def receive_proxied_message(self, proxy_address, msg): log.info('receive-proxied-message', proxy_address=proxy_address, device_id=proxy_address.device_id, msg=hexify(msg)) handler = self.devices_handlers[proxy_address.channel_id] handler.receive_message(msg) def receive_packet_out(self, logical_device_id, egress_port_no, msg): log.info('packet-out', logical_device_id=logical_device_id, egress_port_no=egress_port_no, msg_len=len(msg)) raise NotImplementedError() def receive_inter_adapter_message(self, msg): log.info('rx_inter_adapter_msg') raise NotImplementedError() def suppress_alarm(self, filter): log.info('suppress_alarm', filter=filter) raise NotImplementedError() def unsuppress_alarm(self, filter): log.info('unsuppress_alarm', filter=filter) raise NotImplementedError() def receive_onu_detect_state(self, device_id, state): """ Receive onu detect state in ONU adapter :param proxy_address: ONU device address :param state: ONU detect state (bool) :return: None """ raise NotImplementedError() # PON Mgnt APIs # def create_interface(self, device, data): """ API to create various interfaces (only some PON interfaces as of now) in the devices """ raise NotImplementedError() def update_interface(self, device, data): """ API to update various interfaces (only some PON interfaces as of now) in the devices """ raise NotImplementedError() def remove_interface(self, device, data): """ API to delete various interfaces (only some PON interfaces as of now) in the devices """ raise NotImplementedError() def create_tcont(self, device, tcont_data, traffic_descriptor_data): """ API to create tcont object in the devices :param device: device id :tcont_data: tcont data object :traffic_descriptor_data: traffic descriptor data object :return: None """ log.info('create-tcont', tcont_data=tcont_data, traffic_descriptor_data=traffic_descriptor_data) raise NotImplementedError() def update_tcont(self, device, tcont_data, traffic_descriptor_data): """ API to update tcont object in the devices :param device: device id :tcont_data: tcont data object :traffic_descriptor_data: traffic descriptor data object :return: None """ log.info('update-tcont', tcont_data=tcont_data, traffic_descriptor_data=traffic_descriptor_data) raise NotImplementedError() def remove_tcont(self, device, tcont_data, traffic_descriptor_data): """ API to delete tcont object in the devices :param device: device id :tcont_data: tcont data object :traffic_descriptor_data: traffic descriptor data object :return: None """ log.info('remove-tcont', tcont_data=tcont_data, traffic_descriptor_data=traffic_descriptor_data) raise NotImplementedError() def create_gemport(self, device, data): """ API to create gemport object in the devices :param device: device id :data: gemport data object :return: None """ log.info('create-gemport', data=data) raise NotImplementedError() def update_gemport(self, device, data): """ API to update gemport object in the devices :param device: device id :data: gemport data object :return: None """ log.info('update-gemport', data=data) raise NotImplementedError() def remove_gemport(self, device, data): """ API to delete gemport object in the devices :param device: device id :data: gemport data object :return: None """ log.info('remove-gemport', data=data) raise NotImplementedError() def create_multicast_gemport(self, device, data): raise NotImplementedError() def update_multicast_gemport(self, device, data): raise NotImplementedError() def remove_multicast_gemport(self, device, data): raise NotImplementedError() def create_multicast_distribution_set(self, device, data): raise NotImplementedError() def update_multicast_distribution_set(self, device, data): raise NotImplementedError() def remove_multicast_distribution_set(self, device, data): raise NotImplementedError()
class TibitOnuAdapter(object): name = 'tibit_onu' supported_device_types = [ DeviceType( id='tibit_onu', adapter=name, accepts_bulk_flow_update=True ) ] def __init__(self, adapter_agent, config): self.adapter_agent = adapter_agent self.config = config self.descriptor = Adapter( id=self.name, vendor='Tibit Communications Inc.', version='0.1', config=AdapterConfig(log_level=LogLevel.INFO) ) self.incoming_messages = DeferredQueue() self.mode = "GPON" def start(self): log.debug('starting') log.info('started') def stop(self): log.debug('stopping') log.info('stopped') def adapter_descriptor(self): return self.descriptor def device_types(self): return DeviceTypes(items=self.supported_device_types) def health(self): return HealthStatus(state=HealthStatus.HealthState.HEALTHY) def change_master_state(self, master): raise NotImplementedError() def update_pm_config(self, device, pm_configs): raise NotImplementedError() def adopt_device(self, device): log.info('adopt-device', device=device) reactor.callLater(0.1, self._onu_device_activation, device) return device def reconcile_device(self, device): raise NotImplementedError() @inlineCallbacks def _onu_device_activation(self, device): # 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 # Device information will be updated later on device.vendor = 'Tibit Communications, Inc.' device.model = '10G GPON ONU' device.connect_status = ConnectStatus.REACHABLE self.adapter_agent.update_device(device) # then shortly after we create some ports for the device uni_port = Port( port_no=2, label='UNI facing Ethernet port', type=Port.ETHERNET_UNI, admin_state=AdminState.ENABLED, oper_status=OperStatus.ACTIVE ) self.adapter_agent.add_port(device.id, uni_port) self.adapter_agent.add_port(device.id, Port( port_no=1, label='PON port', type=Port.PON_ONU, admin_state=AdminState.ENABLED, oper_status=OperStatus.ACTIVE, peers=[ Port.PeerPort( device_id=device.parent_id, port_no=device.parent_port_no ) ] )) # TODO adding vports to the logical device shall be done by agent? # then we create the logical device port that corresponds to the UNI # port of the device # obtain logical device id parent_device = self.adapter_agent.get_device(device.parent_id) logical_device_id = parent_device.parent_id assert logical_device_id # we are going to use the proxy_address.channel_id as unique number # and name for the virtual ports, as this is guaranteed to be unique # in the context of the OLT port, so it is also unique in the context # of the logical device port_no = device.proxy_address.channel_id cap = OFPPF_10GB_FD | OFPPF_FIBER self.adapter_agent.add_logical_port(logical_device_id, LogicalPort( id=str(port_no), ofp_port=ofp_port( port_no=port_no, hw_addr=mac_str_to_tuple(device.mac_address), name='uni-{}'.format(port_no), config=0, state=OFPPS_LIVE, curr=cap, advertised=cap, peer=cap, curr_speed=OFPPF_10GB_FD, max_speed=OFPPF_10GB_FD ), device_id=device.id, device_port_no=uni_port.port_no )) # simulate a proxied message sending and receving a reply reply = yield self._message_exchange(device) # TODO - Need to add validation of reply and decide what to do upon failure # and finally update to "ACTIVE" device = self.adapter_agent.get_device(device.id) device.oper_status = OperStatus.ACTIVE self.adapter_agent.update_device(device) # TODO - Disable Stats Reporting for the moment #self.start_kpi_collection(device.id) def abandon_device(self, device): raise NotImplementedError(0 ) def disable_device(self, device): log.info('disabling', device_id=device.id) # Disable all ports on that device self.adapter_agent.disable_all_ports(device.id) # Update the device operational status to UNKNOWN device.oper_status = OperStatus.UNKNOWN device.connect_status = ConnectStatus.UNREACHABLE 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 port_no = device.proxy_address.channel_id # port_id = 'uni-{}'.format(port_no) port_id = '{}'.format(port_no) 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: log.info('logical-port-not-found', device_id=device.id, portid=port_id) # Remove pon port from parent #self.adapter_agent.delete_port_reference_from_parent(device.id, # self.pon_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 log.info('disabled', device_id=device.id) return device def reenable_device(self, device): log.info('re-enabling', device_id=device.id) # 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(device.id) # Add the pon port reference to the parent #self.adapter_agent.add_port_reference_to_parent(device.id, # self.pon_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) logical_device_id = parent_device.parent_id assert logical_device_id port_no = device.proxy_address.channel_id cap = OFPPF_10GB_FD | OFPPF_FIBER self.adapter_agent.add_logical_port(logical_device_id, LogicalPort( # id='uni-{}'.format(port_no), id= str(port_no), ofp_port=ofp_port( port_no=port_no, hw_addr=mac_str_to_tuple(device.mac_address), name='uni-{}'.format(port_no), config=0, state=OFPPS_LIVE, curr=cap, advertised=cap, peer=cap, curr_speed=OFPPF_10GB_FD, max_speed=OFPPF_10GB_FD ), device_id=device.id, device_port_no=2 )) device = self.adapter_agent.get_device(device.id) device.oper_status = OperStatus.ACTIVE self.adapter_agent.update_device(device) log.info('re-enabled', device_id=device.id) @inlineCallbacks def reboot_device(self, device): log.info('Rebooting ONU: {}'.format(device.mac_address)) # Update the operational status to ACTIVATING and connect status to # UNREACHABLE previous_oper_status = device.oper_status previous_conn_status = device.connect_status device.oper_status = OperStatus.ACTIVATING device.connect_status = ConnectStatus.UNREACHABLE self.adapter_agent.update_device(device) msg = ( EOAMPayload() / EOAM_VendSpecificMsg(oui=CableLabs_OUI) / EOAM_DpoeMsg(dpoe_opcode = Dpoe_Opcodes["Set Request"], body=DeviceReset())/ EndOfPDU() ) action = "Device Reset" # send message log.info('ONU-send-proxied-message to {} for ONU: {}'.format(action, device.mac_address)) self.adapter_agent.send_proxied_message(device.proxy_address, msg) rc = [] yield self._handle_set_resp(device, action, rc) # Change the operational status back to its previous state. device.oper_status = previous_oper_status device.connect_status = previous_conn_status self.adapter_agent.update_device(device) log.info('ONU Rebooted: {}'.format(device.mac_address)) def download_image(self, device, request): raise NotImplementedError() def get_image_download_status(self, device, request): raise NotImplementedError() def cancel_image_download(self, device, request): raise NotImplementedError() def activate_image_update(self, device, request): raise NotImplementedError() def revert_image_update(self, device, request): raise NotImplementedError() 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 """ log.info('self-test-device', device=device.id) raise NotImplementedError() def delete_device(self, device): log.info('deleting', device_id=device.id) # A delete request may be received when an OLT is disabled # TODO: # 1) Remove all flows from the device # 2) Remove the device from ponsim log.info('deleted', device_id=device.id) def get_device_details(self, device): raise NotImplementedError() @inlineCallbacks def update_flows_bulk(self, device, flows, groups): log.info('########################################') log.info('bulk-flow-update', device_id=device.id, flows=flows, groups=groups) assert len(groups.items) == 0, "Cannot yet deal with groups" # Only do something if there are flows to program if (len(flows.items) > 0): # Clear the existing entries in the Static MAC Address Table yield self._send_clear_static_mac_table(device) # Re-add the IGMP Multicast Address yield self._send_igmp_mcast_addr(device) Clause = {v: k for k, v in ClauseSubtypeEnum.iteritems()} Operator = {v: k for k, v in RuleOperatorEnum.iteritems()} for flow in flows.items: in_port = get_in_port(flow) assert in_port is not None precedence = 255 - min(flow.priority / 256, 255) if in_port == 2: log.info('#### Upstream Rule ####') up_req = UserPortObject() up_req /= PortIngressRuleHeader(precedence=precedence) for field in get_ofb_fields(flow): if field.type == ETH_TYPE: _type = field.eth_type log.info('#### field.type == ETH_TYPE ####',field_type=_type) elif field.type == IP_PROTO: _proto = field.ip_proto log.info('#### field.type == IP_PROTO ####') elif field.type == IN_PORT: _port = field.port log.info('#### field.type == IN_PORT ####', port=_port) elif field.type == VLAN_VID: _vlan_vid = field.vlan_vid & 0xfff log.info('#### field.type == VLAN_VID ####', vlan=_vlan_vid) up_req /= PortIngressRuleClauseMatchLength02(fieldcode=Clause['C-VLAN Tag'], fieldinstance=0, operator=Operator['=='], match=_vlan_vid) elif field.type == VLAN_PCP: _vlan_pcp = field.vlan_pcp log.info('#### field.type == VLAN_PCP ####', pcp=_vlan_pcp) elif field.type == UDP_DST: _udp_dst = field.udp_dst log.info('#### field.type == UDP_DST ####', udp_dst=_udp_dst) elif field.type == IPV4_DST: _ipv4_dst = field.ipv4_dst log.info('#### field.type == IPV4_DST ####', ipv4_dst=_ipv4_dst) elif field.type == METADATA: _metadata = field.table_metadata log.info('#### field.type == METADATA ####', metadata=_metadata) else: log.info('#### field.type == NOT IMPLEMENTED!! ####') raise NotImplementedError('field.type={}'.format( field.type)) for action in get_actions(flow): if action.type == OUTPUT: log.info('#### action.type == OUTPUT ####') up_req /= PortIngressRuleResultInsert(fieldcode=Clause['C-VLAN Tag']) elif action.type == POP_VLAN: log.info('#### action.type == POP_VLAN ####') elif action.type == PUSH_VLAN: log.info('#### action.type == PUSH_VLAN ####') up_req /= PortIngressRuleResultInsert(fieldcode=Clause['C-VLAN Tag']) # if action.push.ethertype != 0x8100: # log.error('unhandled-tpid', # ethertype=action.push.ethertype) elif action.type == SET_FIELD: log.info('#### action.type == SET_FIELD ####') assert (action.set_field.field.oxm_class == ofp.OFPXMC_OPENFLOW_BASIC) field = action.set_field.field.ofb_field if field.type == VLAN_VID: log.info("#### action.field.vlan {} ####".format(field.vlan_vid & 0xfff)) up_req /= PortIngressRuleResultSet( fieldcode=Clause['C-VLAN Tag'], value=field.vlan_vid & 0xfff) else: log.error('unsupported-action-set-field-type', field_type=field.type) else: log.error('UNSUPPORTED-ACTION-TYPE', action_type=action.type) up_req /= PortIngressRuleTerminator() up_req /= AddPortIngressRule() msg = ( EOAMPayload() / EOAM_VendSpecificMsg(oui=CableLabs_OUI) / EOAM_DpoeMsg(dpoe_opcode = Dpoe_Opcodes["Set Request"], body=up_req)/ EndOfPDU() ) # send message action = "Set ONU US Rule" log.info('ONU-send-proxied-message to {} for ONU: {}'.format(action, device.mac_address)) self.adapter_agent.send_proxied_message(device.proxy_address, msg) # Get and process the Set Response rc = [] yield self._handle_set_resp(device, action, rc) elif in_port == 1: log.info('#### Downstream Rule ####') Is_MCast = False dn_req = PonPortObject() dn_req /= PortIngressRuleHeader(precedence=precedence) #### Loop through fields again... for field in get_ofb_fields(flow): if field.type == ETH_TYPE: _type = field.eth_type log.info('#### field.type == ETH_TYPE ####', in_port=in_port, match=_type) elif field.type == IP_PROTO: _proto = field.ip_proto log.info('#### field.type == IP_PROTO ####', in_port=in_port, ip_proto=_proto) elif field.type == IN_PORT: _port = field.port log.info('#### field.type == IN_PORT ####') elif field.type == VLAN_VID: _vlan_vid = field.vlan_vid & 0xfff log.info('#### field.type == VLAN_VID ####') elif field.type == VLAN_PCP: _vlan_pcp = field.vlan_pcp log.info('#### field.type == VLAN_PCP ####') elif field.type == UDP_DST: _udp_dst = field.udp_dst log.info('#### field.type == UDP_DST ####') elif field.type == IPV4_DST: _ipv4_dst = field.ipv4_dst log.info('#### field.type == IPV4_DST ####') a = int(hex(_ipv4_dst)[2:4], 16) b = int(hex(_ipv4_dst)[4:6], 16) c = int(hex(_ipv4_dst)[6:8], 16) d = int(hex(_ipv4_dst)[8:], 16) dn_req = AddStaticMacAddress(mac=mcastIp2McastMac('%d.%d.%d.%d' % (a,b,c,d))) Is_MCast = True else: raise NotImplementedError('field.type={}'.format( field.type)) for action in get_actions(flow): if action.type == OUTPUT: log.info('#### action.type == OUTPUT ####') elif action.type == POP_VLAN: log.info('#### action.type == POP_VLAN ####') dn_req /= PortIngressRuleClauseMatchLength02(fieldcode=Clause['C-VLAN Tag'], fieldinstance=0, operator=Operator['=='], match=_vlan_vid) dn_req /= PortIngressRuleResultReplace(fieldcode=Clause['C-VLAN Tag']) dn_req /= PortIngressRuleResultSet( fieldcode=Clause['C-VLAN Tag'], value=field.vlan_vid & 0xfff) elif action.type == PUSH_VLAN: log.info('#### action.type == PUSH_VLAN ####') if action.push.ethertype != 0x8100: log.error('unhandled-ether-type', ethertype=action.push.ethertype) elif action.type == SET_FIELD: log.info('#### action.type == SET_FIELD ####') assert (action.set_field.field.oxm_class == ofp.OFPXMC_OPENFLOW_BASIC) field = action.set_field.field.ofb_field if field.type == VLAN_VID: dn_req /= PortIngressRuleClauseMatchLength02(fieldcode=Clause['C-VLAN Tag'], fieldinstance=0, operator=Operator['=='], match=_vlan_vid) dn_req /= PortIngressRuleResultReplace(fieldcode=Clause['C-VLAN Tag']) dn_req /= PortIngressRuleResultSet( fieldcode=Clause['C-VLAN Tag'], value=field.vlan_vid & 0xfff) else: log.error('unsupported-action-set-field-type', field_type=field.type) else: log.error('UNSUPPORTED-ACTION-TYPE', action_type=action.type) if Is_MCast is True: action = "Set Static IP MCAST address" else: dn_req /= PortIngressRuleTerminator() dn_req /= AddPortIngressRule() action = "Set ONU DS Rule" msg = ( EOAMPayload() / EOAM_VendSpecificMsg(oui=CableLabs_OUI) / EOAM_DpoeMsg(dpoe_opcode = Dpoe_Opcodes["Set Request"], body=dn_req)/ EndOfPDU() ) # send message log.info('ONU-send-proxied-message to {} for ONU: {}'.format(action, device.mac_address)) self.adapter_agent.send_proxied_message(device.proxy_address, msg) # Get and process the Set Response rc = [] yield self._handle_set_resp(device, action, rc) else: raise Exception('Port should be 1 or 2 by our convention') log.info('bulk-flow-update finished', device_id=device.id, flows=flows, groups=groups) log.info('########################################') def update_flows_incrementally(self, device, flow_changes, group_changes): raise NotImplementedError() def send_proxied_message(self, proxy_address, msg): raise NotImplementedError() def receive_proxied_message(self, proxy_address, msg): log.info('receive-proxied-message', proxy_address=proxy_address, msg=msg.show(dump=True)) self.incoming_messages.put(msg) def create_interface(self, device, data): raise NotImplementedError() def update_interface(self, device, data): raise NotImplementedError() def remove_interface(self, device, data): raise NotImplementedError() def receive_onu_detect_state(self, device_id, state): raise NotImplementedError() def create_tcont(self, device, tcont_data, traffic_descriptor_data): raise NotImplementedError() def update_tcont(self, device, tcont_data, traffic_descriptor_data): raise NotImplementedError() def remove_tcont(self, device, tcont_data, traffic_descriptor_data): raise NotImplementedError() def create_gemport(self, device, data): raise NotImplementedError() def update_gemport(self, device, data): raise NotImplementedError() def remove_gemport(self, device, data): raise NotImplementedError() def create_multicast_gemport(self, device, data): raise NotImplementedError() def update_multicast_gemport(self, device, data): raise NotImplementedError() def remove_multicast_gemport(self, device, data): raise NotImplementedError() def create_multicast_distribution_set(self, device, data): raise NotImplementedError() def update_multicast_distribution_set(self, device, data): raise NotImplementedError() def remove_multicast_distribution_set(self, device, data): raise NotImplementedError() @inlineCallbacks def _message_exchange(self, device): # register for receiving async messages self.adapter_agent.register_for_proxied_messages(device.proxy_address) # reset incoming message queue while self.incoming_messages.pending: _ = yield self.incoming_messages.get() # send out ping frame to ONU device get device information ping_frame = ( EOAMPayload() / EOAM_VendSpecificMsg(oui=CableLabs_OUI) / EOAM_DpoeMsg(dpoe_opcode=Dpoe_Opcodes["Get Request"], body=VendorName() / OnuMode() / HardwareVersion() / ManufacturerInfo() ) / EndOfPDU() ) log.info('ONU-send-proxied-message to Get Version Info for ONU: {}'.format(device.mac_address)) self.adapter_agent.send_proxied_message(device.proxy_address, ping_frame) # Loop until we have a Get Response ack = False while not ack: frame = yield self.incoming_messages.get() respType = get_oam_msg_type(log, frame) if (respType == RxedOamMsgTypeEnum["DPoE Get Response"]): ack = True else: # Handle unexpected events/OMCI messages check_resp(log, frame) if ack: log.info('ONU-response received for Get Version Info for ONU: {}'.format(device.mac_address)) self._process_ping_frame_response(device, frame) if self.mode.upper()[0] == "G": # GPON hw_vers = int(device.hardware_version, 16) if hw_vers >= 0x170618: mcastLidx = 0x04bc elif hw_vers >= 0x170517: mcastLidx = 0x14bc else: mcastLidx = 0x10bc log.info("Using Multicast LIDX {:04X}".format(mcastLidx)) # construct multicast LLID set msg = ( EOAMPayload() / EOAM_VendSpecificMsg(oui=CableLabs_OUI) / EOAM_DpoeMsg(dpoe_opcode=Dpoe_Opcodes["Multicast Register"],body=MulticastRegisterSet(MulticastLink=mcastLidx, UnicastLink=0) )) # send message log.info('ONU-send-proxied-message to Multicast Register Set for ONU: {}'.format(device.mac_address)) self.adapter_agent.send_proxied_message(device.proxy_address, msg) # The MulticastRegisterSet does not currently return a response. Just hope it worked. # by returning we allow the device to be shown as active, which # indirectly verified that message passing works def receive_packet_out(self, logical_device_id, egress_port_no, msg): log.info('packet-out', logical_device_id=logical_device_id, egress_port_no=egress_port_no, msg_len=len(msg)) def receive_inter_adapter_message(self, msg): raise NotImplementedError() def suppress_alarm(self, filter): raise NotImplementedError() def unsuppress_alarm(self, filter): raise NotImplementedError() def start_kpi_collection(self, device_id): """TMP Simulate periodic KPI metric collection from the device""" import random @inlineCallbacks # pretend that we need to do async calls def _collect(device_id, prefix): try: # Step 1: gather metrics from device (pretend it here) - examples uni_port_metrics = yield dict( tx_pkts=random.randint(0, 100), rx_pkts=random.randint(0, 100), tx_bytes=random.randint(0, 100000), rx_bytes=random.randint(0, 100000), ) pon_port_metrics = yield dict( tx_pkts=uni_port_metrics['rx_pkts'], rx_pkts=uni_port_metrics['tx_pkts'], tx_bytes=uni_port_metrics['rx_bytes'], rx_bytes=uni_port_metrics['tx_bytes'], ) onu_metrics = yield dict( cpu_util=20 + 5 * random.random(), buffer_util=10 + 10 * random.random() ) # 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={ # OLT-level prefix: MetricValuePairs(metrics=onu_metrics), # OLT NNI port prefix + '.nni': MetricValuePairs(metrics=uni_port_metrics), # OLT PON port prefix + '.pon': MetricValuePairs(metrics=pon_port_metrics) } ) # Step 3: submit self.adapter_agent.submit_kpis(kpi_event) except Exception as e: log.exception('failed-to-submit-kpis', e=e) prefix = 'voltha.{}.{}'.format(self.name, device_id) lc = LoopingCall(_collect, device_id, prefix) lc.start(interval=15) # TODO make this configurable # Methods for Get / Set Response Processing from eoam_messages @inlineCallbacks def _send_igmp_mcast_addr(self, device): # construct install of igmp query address msg = ( EOAMPayload() / EOAM_VendSpecificMsg(oui=CableLabs_OUI) / EOAM_DpoeMsg(dpoe_opcode=Dpoe_Opcodes["Set Request"],body=AddStaticMacAddress(mac='01:00:5e:00:00:01') )) action = "Set Static IGMP MAC address" # send message log.info('ONU-send-proxied-message to {} for ONU: {}'.format(action, device.mac_address)) self.adapter_agent.send_proxied_message(device.proxy_address, msg) rc = [] yield self._handle_set_resp(device, action, rc) @inlineCallbacks def _send_clear_static_mac_table(self, device): # construct install of igmp query address msg = ( EOAMPayload() / EOAM_VendSpecificMsg(oui=CableLabs_OUI) / EOAM_DpoeMsg(dpoe_opcode=Dpoe_Opcodes["Set Request"],body=ClearStaticMacTable() )) action = "Clear Static MAC Table" # send message log.info('ONU-send-proxied-message to {} for ONU: {}'.format(action, device.mac_address)) self.adapter_agent.send_proxied_message(device.proxy_address, msg) rc = [] yield self._handle_set_resp(device, action, rc) @inlineCallbacks def _handle_set_resp(self, device, action, retcode): # Get and process the Set Response ack = False start_time = time.time() # Loop until we have a set response or timeout while not ack: frame = yield self.incoming_messages.get() #TODO - Need to add propoer timeout functionality #if (time.time() - start_time) > TIBIT_MSG_WAIT_TIME or (frame is None): # break # don't wait forever respType = get_oam_msg_type(log, frame) #Check that the message received is a Set Response if (respType == RxedOamMsgTypeEnum["DPoE Set Response"]): ack = True else: log.info('Received Unexpected OAM Message 0x{:X} while waiting for Set Resp for {}'.format(respType,action)) # Handle unexpected events/OMCI messages check_resp(log, frame) # Verify Set Response rc = False if ack: (rc,branch,leaf,status) = check_set_resp(log, frame) if (rc is False): log.info('Set Response had errors - Branch 0x{:X} Leaf 0x{:0>4X} {}'.format(branch, leaf, DPoEVariableResponseCodes[status])) if (rc is True): log.info('ONU-response received for {} for ONU: {}'.format(action, device.mac_address)) else: log.info('BAD ONU-response received for {} for ONU: {}'.format(action, device.mac_address)) retcode.append(rc) def _process_ping_frame_response(self, device, frame): vendor = [0xD7, 0x0011] ponMode = [0xB7, 0x0105] hw_version = [0xD7, 0x0013] manufacturer = [0xD7, 0x0006] branch_leaf_pairs = [vendor, ponMode, hw_version, manufacturer] for pair in branch_leaf_pairs: temp_pair = pair (rc, value) = (get_value_from_msg(log, frame, pair[0], pair[1])) temp_pair.append(rc) temp_pair.append(value) if rc: overall_rc = True else: log.info('Failed to get valid response for Branch 0x{:X} Leaf 0x{:0>4X} '.format(temp_pair[0], temp_pair[1])) ack = True if vendor[rc]: device.vendor = vendor.pop() if device.vendor.endswith(''): device.vendor = device.vendor[:-1] else: device.vendor = "UNKNOWN" # mode: 3 = EPON OLT, 7 = GPON OLT # mode: 2 = EPON ONU, 6 = GPON ONU if ponMode[rc]: value = ponMode.pop() mode = "UNKNOWN" self.mode = "UNKNOWN" if value == 6: mode = "10G GPON ONU" self.mode = "GPON" if value == 2: mode = "10G EPON ONU" self.mode = "EPON" if value == 1: mode = "10G Point to Point" self.mode = "Unsupported" device.model = mode else: device.model = "UNKNOWN" self.mode = "UNKNOWN" log.info("PON Mode is {}".format(self.mode)) if hw_version[rc]: device.hardware_version = hw_version.pop() device.hardware_version = device.hardware_version.replace("FA","") if device.hardware_version.endswith(''): device.hardware_version = device.hardware_version[:-1] else: device.hardware_version = "UNKNOWN" if manufacturer[rc]: manu_value = manufacturer.pop() device.firmware_version = re.search('\Firmware: (.+?) ', manu_value).group(1) image_1 = Image(version = \ re.search('\Build: (.+?) ', manu_value).group(1)) device.images.image.extend([ image_1 ]) device.serial_number = re.search('\Serial #: (.+?) ', manu_value).group(1) else: device.firmware_version = "UNKNOWN" image_1 = Image(version="UNKNOWN") device.images.image.extend([ image_1 ]) device.serial_number = "UNKNOWN" device.connect_status = ConnectStatus.REACHABLE
class DPoEOnuAdapter(object): name = 'dpoe_onu' supported_device_types = [ DeviceType(id='dpoe_onu', adapter=name, accepts_bulk_flow_update=True) ] def __init__(self, adapter_agent, config): self.adapter_agent = adapter_agent self.config = config self.descriptor = Adapter( id=self.name, vendor='Sumitomo Electric, Inc.', version='0.1', config=AdapterConfig(log_level=LogLevel.INFO)) self.incoming_messages = DeferredQueue() def start(self): log.debug('starting') log.info('started') def stop(self): log.debug('stopping') log.info('stopped') def adapter_descriptor(self): return self.descriptor def device_types(self): return DeviceTypes(items=self.supported_device_types) def health(self): return HealthStatus(state=HealthStatus.HealthState.HEALTHY) def change_master_state(self, master): raise NotImplementedError() def update_pm_config(self, device, pm_configs): raise NotImplementedError() def adopt_device(self, device): log.info('adopt-device', device=device) reactor.callLater(0.1, self._onu_device_activation, device) return device def reconcile_device(self, device): raise NotImplementedError() @inlineCallbacks def _onu_device_activation(self, device): # 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 # TODO: For now, pretend that we were able to contact the device and obtain # additional information about it. Should add real message. device.vendor = 'Sumitomo Electric, Inc.' device.model = '10G EPON ONU' device.hardware_version = 'fa161020' device.firmware_version = '16.12.02' # There could be multiple software versions on the device (one active, other # standby etc.). Look for simulated_olt for example implementation. device.images.image.extend([Image(version="1.0")]) device.serial_number = uuid4().hex device.connect_status = ConnectStatus.REACHABLE self.adapter_agent.update_device(device) # then shortly after we create some ports for the device uni_port = Port(port_no=2, label='UNI facing Ethernet port', type=Port.ETHERNET_UNI, admin_state=AdminState.ENABLED, oper_status=OperStatus.ACTIVE) self.adapter_agent.add_port(device.id, uni_port) self.adapter_agent.add_port( device.id, Port(port_no=1, label='PON port', type=Port.PON_ONU, admin_state=AdminState.ENABLED, oper_status=OperStatus.ACTIVE, peers=[ Port.PeerPort(device_id=device.parent_id, port_no=device.parent_port_no) ])) # TODO adding vports to the logical device shall be done by agent? # then we create the logical device port that corresponds to the UNI # port of the device # obtain logical device id parent_device = self.adapter_agent.get_device(device.parent_id) logical_device_id = parent_device.parent_id assert logical_device_id # we are going to use the proxy_address.channel_id as unique number # and name for the virtual ports, as this is guaranteed to be unique # in the context of the OLT port, so it is also unique in the context # of the logical device port_no = device.proxy_address.channel_id cap = OFPPF_10GB_FD | OFPPF_FIBER self.adapter_agent.add_logical_port( logical_device_id, LogicalPort(id=str(port_no), ofp_port=ofp_port(port_no=port_no, hw_addr=mac_str_to_tuple( device.mac_address), name='uni-{}'.format(port_no), config=0, state=OFPPS_LIVE, curr=cap, advertised=cap, peer=cap, curr_speed=OFPPF_10GB_FD, max_speed=OFPPF_10GB_FD), device_id=device.id, device_port_no=uni_port.port_no)) # simulate a proxied message sending and receving a reply reply = yield self._message_exchange(device) # and finally update to "ACTIVE" device = self.adapter_agent.get_device(device.id) device.oper_status = OperStatus.ACTIVE self.adapter_agent.update_device(device) def abandon_device(self, device): raise NotImplementedError(0) def disable_device(self, device): raise NotImplementedError() def reenable_device(self, device): raise NotImplementedError() def reboot_device(self, device): raise NotImplementedError() def download_image(self, device, request): raise NotImplementedError() def get_image_download_status(self, device, request): raise NotImplementedError() def cancel_image_download(self, device, request): raise NotImplementedError() def activate_image_update(self, device, request): raise NotImplementedError() def revert_image_update(self, device, request): raise NotImplementedError() 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 """ log.info('self-test-device', device=device.id) raise NotImplementedError() def delete_device(self, device): raise NotImplementedError() def get_device_details(self, device): raise NotImplementedError() def update_flows_bulk(self, device, flows, groups): log.info('########################################') log.info('bulk-flow-update', device_id=device.id, flows=flows, groups=groups) assert len(groups.items) == 0, "Cannot yet deal with groups" Clause = {v: k for k, v in ClauseSubtypeEnum.iteritems()} Operator = {v: k for k, v in RuleOperatorEnum.iteritems()} for flow in flows.items: in_port = get_in_port(flow) assert in_port is not None precedence = 255 - min(flow.priority / 256, 255) if in_port == 2: log.info('#### Downstream Rule ####') dn_req = EOAMPayload(body=CablelabsOUI() / DPoEOpcode_SetRequest()) for field in get_ofb_fields(flow): if field.type == ETH_TYPE: _type = field.eth_type log.info('#### field.type == ETH_TYPE ####', field_type=_type) elif field.type == IP_PROTO: _proto = field.ip_proto log.info('#### field.type == IP_PROTO ####') pass # construct ip_proto based condition here elif field.type == IN_PORT: _port = field.port log.info('#### field.type == IN_PORT ####', port=_port) pass # construct in_port based condition here elif field.type == VLAN_VID: _vlan_vid = field.vlan_vid & 0xfff log.info('#### field.type == VLAN_VID ####', vlan=_vlan_vid) elif field.type == VLAN_PCP: _vlan_pcp = field.vlan_pcp log.info('#### field.type == VLAN_PCP ####', pcp=_vlan_pcp) pass # construct VLAN PCP based filter condition here elif field.type == UDP_DST: _udp_dst = field.udp_dst log.info('#### field.type == UDP_DST ####') pass # construct UDP SDT based filter here elif field.type == IPV4_DST: _ipv4_dst = field.ipv4_dst log.info('#### field.type == IPV4_DST ####') pass else: log.info('#### field.type == NOT IMPLEMENTED!! ####') raise NotImplementedError('field.type={}'.format( field.type)) for action in get_actions(flow): if action.type == OUTPUT: log.info('#### action.type == OUTPUT ####') elif action.type == POP_VLAN: log.info('#### action.type == POP_VLAN ####') pass # construct vlan pop command here elif action.type == PUSH_VLAN: log.info('#### action.type == PUSH_VLAN ####') if action.push.ethertype != 0x8100: log.error('unhandled-tpid', ethertype=action.push.ethertype) elif action.type == SET_FIELD: log.info('#### action.type == SET_FIELD ####') assert (action.set_field.field.oxm_class == ofp.OFPXMC_OPENFLOW_BASIC) field = action.set_field.field.ofb_field if field.type == VLAN_VID: pass else: log.error('unsupported-action-set-field-type', field_type=field.type) else: log.error('UNSUPPORTED-ACTION-TYPE', action_type=action.type) # send message log.info('ONU-send-proxied-message') # self.adapter_agent.send_proxied_message(device.proxy_address, dn_req) elif in_port == 1: # Upstream rule log.info('#### Upstream Rule ####') #### Loop through fields again... for field in get_ofb_fields(flow): if field.type == ETH_TYPE: _type = field.eth_type log.info('#### field.type == ETH_TYPE ####', in_port=in_port, match=_type) elif field.type == IP_PROTO: _proto = field.ip_proto log.info('#### field.type == IP_PROTO ####', in_port=in_port, ip_proto=ip_proto) elif field.type == IN_PORT: _port = field.port log.info('#### field.type == IN_PORT ####') pass # construct in_port based condition here elif field.type == VLAN_VID: _vlan_vid = field.vlan_vid & 0xfff log.info('#### field.type == VLAN_VID ####') elif field.type == VLAN_PCP: _vlan_pcp = field.vlan_pcp log.info('#### field.type == VLAN_PCP ####') pass # construct VLAN PCP based filter condition here elif field.type == UDP_DST: _udp_dst = field.udp_dst log.info('#### field.type == UDP_DST ####') else: raise NotImplementedError('field.type={}'.format( field.type)) for action in get_actions(flow): if action.type == OUTPUT: log.info('#### action.type == OUTPUT ####') elif action.type == POP_VLAN: log.info('#### action.type == POP_VLAN ####') pass # construct vlan pop command here elif action.type == PUSH_VLAN: log.info('#### action.type == PUSH_VLAN ####') if action.push.ethertype != 0x8100: log.error('unhandled-ether-type', ethertype=action.push.ethertype) elif action.type == SET_FIELD: log.info('#### action.type == SET_FIELD ####') assert (action.set_field.field.oxm_class == ofp.OFPXMC_OPENFLOW_BASIC) field = action.set_field.field.ofb_field if field.type == VLAN_VID: pass else: log.error('unsupported-action-set-field-type', field_type=field.type) else: log.error('UNSUPPORTED-ACTION-TYPE', action_type=action.type) else: raise Exception('Port should be 1 or 2 by our convention') def update_flows_incrementally(self, device, flow_changes, group_changes): raise NotImplementedError() def send_proxied_message(self, proxy_address, msg): raise NotImplementedError() def receive_proxied_message(self, proxy_address, msg): log.debug('receive-proxied-message', proxy_address=proxy_address, msg=msg) self.incoming_messages.put(msg) def create_interface(self, device, data): raise NotImplementedError() def update_interface(self, device, data): raise NotImplementedError() def remove_interface(self, device, data): raise NotImplementedError() def receive_onu_detect_state(self, device_id, state): raise NotImplementedError() def create_tcont(self, device, tcont_data, traffic_descriptor_data): raise NotImplementedError() def update_tcont(self, device, tcont_data, traffic_descriptor_data): raise NotImplementedError() def remove_tcont(self, device, tcont_data, traffic_descriptor_data): raise NotImplementedError() def create_gemport(self, device, data): raise NotImplementedError() def update_gemport(self, device, data): raise NotImplementedError() def remove_gemport(self, device, data): raise NotImplementedError() def create_multicast_gemport(self, device, data): raise NotImplementedError() def update_multicast_gemport(self, device, data): raise NotImplementedError() def remove_multicast_gemport(self, device, data): raise NotImplementedError() def create_multicast_distribution_set(self, device, data): raise NotImplementedError() def update_multicast_distribution_set(self, device, data): raise NotImplementedError() def remove_multicast_distribution_set(self, device, data): raise NotImplementedError() def suppress_alarm(self, filter): raise NotImplementedError() def unsuppress_alarm(self, filter): raise NotImplementedError() @inlineCallbacks def _message_exchange(self, device): # register for receiving async messages self.adapter_agent.register_for_proxied_messages(device.proxy_address) # reset incoming message queue while self.incoming_messages.pending: _ = yield self.incoming_messages.get() # construct message msg = EOAMPayload(body=CablelabsOUI() / DPoEOpcode_GetRequest() / DeviceId()) # send message log.info('ONU-send-proxied-message') self.adapter_agent.send_proxied_message(device.proxy_address, msg) # wait till we detect incoming message yield self.incoming_messages.get() # construct install of igmp query address msg = EOAMPayload(body=CablelabsOUI() / DPoEOpcode_SetRequest() / AddStaticMacAddress(mac='01:00:5e:00:00:01')) # send message log.info('ONU-send-proxied-message') self.adapter_agent.send_proxied_message(device.proxy_address, msg) # wait till we detect incoming message yield self.incoming_messages.get() # construct install of igmp query address msg = EOAMPayload(body=CablelabsOUI() / DPoEOpcode_GetRequest() / FirmwareInfo()) # send message log.info('ONU-send-proxied-message') self.adapter_agent.send_proxied_message(device.proxy_address, msg) # wait till we detect incoming message yield self.incoming_messages.get() # by returning we allow the device to be shown as active, which # indirectly verified that message passing works def receive_packet_out(self, logical_device_id, egress_port_no, msg): log.info('packet-out', logical_device_id=logical_device_id, egress_port_no=egress_port_no, msg_len=len(msg)) def receive_inter_adapter_message(self, msg): raise NotImplementedError()
class BrcmOpenomciOnuAdapter(object): name = 'brcm_openomci_onu' supported_device_types = [ DeviceType(id=name, vendor_ids=[ 'OPEN', 'ALCL', 'BRCM', 'TWSH', 'ALPH', 'ISKT', 'SFAA', 'BBSM' ], adapter=name, accepts_bulk_flow_update=True) ] def __init__(self, adapter_agent, config): log.debug('function-entry', config=config) self.adapter_agent = adapter_agent self.config = config self.descriptor = Adapter( id=self.name, vendor='Voltha project', version='0.50', config=AdapterConfig(log_level=LogLevel.INFO)) self.devices_handlers = dict() # Customize OpenOMCI for Broadcom ONUs self.broadcom_omci = deepcopy(OpenOmciAgentDefaults) self.broadcom_omci['mib-synchronizer'][ 'state-machine'] = BrcmMibSynchronizer self.broadcom_omci['omci-capabilities']['tasks'][ 'get-capabilities'] = BrcmCapabilitiesTask # Defer creation of omci agent to a lazy init that allows subclasses to override support classes # register for adapter messages self.adapter_agent.register_for_inter_adapter_messages() def custom_me_entities(self): return None @property def omci_agent(self): if not hasattr(self, '_omci_agent') or self._omci_agent is None: log.debug('creating-omci-agent') self._omci_agent = OpenOMCIAgent( self.adapter_agent.core, support_classes=self.broadcom_omci) return self._omci_agent def start(self): log.debug('starting') self.omci_agent.start() log.info('started') def stop(self): log.debug('stopping') omci, self._omci_agent = self._omci_agent, None if omci is not None: self._omci_agent.stop() log.info('stopped') def adapter_descriptor(self): return self.descriptor def device_types(self): return DeviceTypes(items=self.supported_device_types) def health(self): return HealthStatus(state=HealthStatus.HealthState.HEALTHY) def change_master_state(self, master): raise NotImplementedError() def adopt_device(self, device): log.info('adopt_device', device_id=device.id) self.devices_handlers[device.id] = BrcmOpenomciOnuHandler( self, device.id) reactor.callLater(0, self.devices_handlers[device.id].activate, device) return device def reconcile_device(self, device): log.info('reconcile-device', device_id=device.id) self.devices_handlers[device.id] = BrcmOpenomciOnuHandler( self, device.id) reactor.callLater(0, self.devices_handlers[device.id].reconcile, device) def abandon_device(self, device): raise NotImplementedError() def disable_device(self, device): log.info('disable-onu-device', device_id=device.id) if device.id in self.devices_handlers: handler = self.devices_handlers[device.id] if handler is not None: handler.disable(device) def reenable_device(self, device): log.info('reenable-onu-device', device_id=device.id) if device.id in self.devices_handlers: handler = self.devices_handlers[device.id] if handler is not None: handler.reenable(device) def reboot_device(self, device): log.info('reboot-device', device_id=device.id) if device.id in self.devices_handlers: handler = self.devices_handlers[device.id] if handler is not None: handler.reboot() def download_image(self, device, request): raise NotImplementedError() def get_image_download_status(self, device, request): raise NotImplementedError() def cancel_image_download(self, device, request): raise NotImplementedError() def activate_image_update(self, device, request): raise NotImplementedError() def revert_image_update(self, device, request): raise NotImplementedError() 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 """ log.info('self-test-device - Not implemented yet', device=device.id) raise NotImplementedError() def delete_device(self, device): log.info('delete-device', device_id=device.id) if device.id in self.devices_handlers: handler = self.devices_handlers[device.id] if handler is not None: handler.delete(device) del self.devices_handlers[device.id] return def get_device_details(self, device): raise NotImplementedError() # TODO(smbaker): When BrcmOpenomciOnuAdapter is updated to inherit from OnuAdapter, this function can be deleted def update_pm_config(self, device, pm_config): log.info("adapter-update-pm-config", device=device, pm_config=pm_config) handler = self.devices_handlers[device.id] handler.update_pm_config(device, pm_config) def update_flows_bulk(self, device, flows, groups): ''' log.info('bulk-flow-update', device_id=device.id, flows=flows, groups=groups) ''' assert len(groups.items) == 0 handler = self.devices_handlers[device.id] return handler.update_flow_table(device, flows.items) def update_flows_incrementally(self, device, flow_changes, group_changes): raise NotImplementedError() def send_proxied_message(self, proxy_address, msg): log.debug('send-proxied-message', proxy_address=proxy_address, msg=msg) def receive_proxied_message(self, proxy_address, msg): log.debug('receive-proxied-message', proxy_address=proxy_address, device_id=proxy_address.device_id, msg=hexify(msg)) # Device_id from the proxy_address is the olt device id. We need to # get the onu device id using the port number in the proxy_address device = self.adapter_agent. \ get_child_device_with_proxy_address(proxy_address) if device: handler = self.devices_handlers[device.id] handler.receive_message(msg) def receive_packet_out(self, logical_device_id, egress_port_no, msg): log.info('packet-out', logical_device_id=logical_device_id, egress_port_no=egress_port_no, msg_len=len(msg)) def receive_inter_adapter_message(self, msg): log.debug('receive_inter_adapter_message', msg=msg) proxy_address = msg['proxy_address'] assert proxy_address is not None # Device_id from the proxy_address is the olt device id. We need to # get the onu device id using the port number in the proxy_address device = self.adapter_agent. \ get_child_device_with_proxy_address(proxy_address) if device: handler = self.devices_handlers[device.id] handler.event_messages.put(msg) else: log.error("device-not-found") def create_interface(self, device, data): log.debug('create-interface', device_id=device.id) if device.id in self.devices_handlers: handler = self.devices_handlers[device.id] if handler is not None: handler.create_interface(data) def update_interface(self, device, data): log.debug('update-interface', device_id=device.id) if device.id in self.devices_handlers: handler = self.devices_handlers[device.id] if handler is not None: handler.update_interface(data) def remove_interface(self, device, data): log.debug('remove-interface', device_id=device.id) if device.id in self.devices_handlers: handler = self.devices_handlers[device.id] if handler is not None: handler.remove_interface(data) def receive_onu_detect_state(self, device_id, state): raise NotImplementedError() def create_tcont(self, device, tcont_data, traffic_descriptor_data): log.debug('create-tcont', device_id=device.id) if device.id in self.devices_handlers: handler = self.devices_handlers[device.id] if handler is not None: handler.create_tcont(tcont_data, traffic_descriptor_data) def update_tcont(self, device, tcont_data, traffic_descriptor_data): raise NotImplementedError() def remove_tcont(self, device, tcont_data, traffic_descriptor_data): log.debug('remove-tcont', device_id=device.id) if device.id in self.devices_handlers: handler = self.devices_handlers[device.id] if handler is not None: handler.remove_tcont(tcont_data, traffic_descriptor_data) def create_gemport(self, device, data): log.debug('create-gemport', device_id=device.id) if device.id in self.devices_handlers: handler = self.devices_handlers[device.id] if handler is not None: handler.create_gemport(data) def update_gemport(self, device, data): raise NotImplementedError() def remove_gemport(self, device, data): log.debug('remove-gemport', device_id=device.id) if device.id in self.devices_handlers: handler = self.devices_handlers[device.id] if handler is not None: handler.remove_gemport(data) def create_multicast_gemport(self, device, data): log.debug('create-multicast-gemport', device_id=device.id) if device.id in self.devices_handlers: handler = self.devices_handlers[device.id] if handler is not None: handler.create_multicast_gemport(data) def update_multicast_gemport(self, device, data): raise NotImplementedError() def remove_multicast_gemport(self, device, data): raise NotImplementedError() def create_multicast_distribution_set(self, device, data): raise NotImplementedError() def update_multicast_distribution_set(self, device, data): raise NotImplementedError() def remove_multicast_distribution_set(self, device, data): raise NotImplementedError() def suppress_alarm(self, filter): raise NotImplementedError() def unsuppress_alarm(self, filter): raise NotImplementedError()
class PmcsOnu(object): name = 'pmcs_onu' supported_device_types = [ DeviceType(id=name, adapter=name, accepts_bulk_flow_update=True) ] def __init__(self, adapter_agent, config): self.adapter_agent = adapter_agent self.config = config self.descriptor = Adapter( id=self.name, vendor='PMCS', version='0.1', config=AdapterConfig(log_level=LogLevel.INFO)) self.incoming_messages = DeferredQueue() self.trangen = sequence_generator(1) def start(self): log.debug('starting') log.info('started') def stop(self): log.debug('stopping') log.info('stopped') def adapter_descriptor(self): return self.descriptor def device_types(self): return DeviceTypes(items=self.supported_device_types) def health(self): return HealthStatus(state=HealthStatus.HealthState.HEALTHY) def change_master_state(self, master): raise NotImplementedError() def adopt_device(self, device): log.info('adopt-device', device=device) reactor.callLater(0.1, self._onu_device_activation, device) return device def abandon_device(self, device): raise NotImplementedError() def disable_device(self, device): raise NotImplementedError() def reenable_device(self, device): raise NotImplementedError() def reboot_device(self, device): raise NotImplementedError() def delete_device(self, device): raise NotImplementedError() def get_device_details(self, device): raise NotImplementedError() def deactivate_device(self, device): raise NotImplementedError() def update_pm_config(self, device, pm_configs): raise NotImplementedError() def update_flows_bulk(self, device, flows, groups): log.info('bulk-flow-update', device_id=device.id, flows=flows, groups=groups) def update_flows_incrementally(self, device, flow_changes, group_changes): raise NotImplementedError() def send_proxied_message(self, proxy_address, msg): log.info('send-proxied-message', proxy_address=proxy_address, msg=msg) def receive_proxied_message(self, proxy_address, msg): log.info('receive-proxied-message', proxy_address=proxy_address, device_id=proxy_address.device_id) self.incoming_messages.put(msg) def receive_packet_out(self, logical_device_id, egress_port_no, msg): log.info('packet-out', logical_device_id=logical_device_id, egress_port_no=egress_port_no, msg_len=len(msg)) @inlineCallbacks def _onu_device_activation(self, device): # 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 == 0 device.model = 'GPON ONU' device.hardware_version = 'tbd' device.firmware_version = 'tbd' device.software_version = 'tbd' device.connect_status = ConnectStatus.REACHABLE self.adapter_agent.update_device(device) uni_port = Port( port_no=1000, # FIXME It becomes the alloc_id label="{} ONU".format('PMCS'), type=Port.ETHERNET_UNI, admin_state=AdminState.ENABLED, oper_status=OperStatus.ACTIVE) self.adapter_agent.add_port(device.id, uni_port) pon_port = Port(port_no=1, label='PON port', type=Port.PON_ONU, admin_state=AdminState.ENABLED, oper_status=OperStatus.ACTIVE, peers=[ Port.PeerPort(device_id=device.parent_id, port_no=device.parent_port_no) ]) self.adapter_agent.add_port(device.id, pon_port) # obtain logical device id parent_device = self.adapter_agent.get_device(device.parent_id) logical_device_id = parent_device.parent_id assert logical_device_id # we are going to use the proxy_address.channel_id as unique number # and name for the virtual ports, as this is guaranteed to be unique # in the context of the OLT port, so it is also unique in the context # of the logical device port_no = device.proxy_address.channel_id # FIXME this may need to be fixed cap = OFPPF_1GB_FD | OFPPF_FIBER self.adapter_agent.add_logical_port( logical_device_id, LogicalPort(id=str(port_no), ofp_port=ofp_port(port_no=port_no, hw_addr=mac_str_to_tuple( device.serial_number)[2:8], name='uni-{}'.format(port_no), config=0, state=OFPPS_LIVE, curr=cap, advertised=cap, peer=cap, curr_speed=OFPPF_1GB_FD, max_speed=OFPPF_1GB_FD), device_id=device.id, device_port_no=uni_port.port_no)) yield self._initialize_onu(device) # and finally update to "ACTIVE" device = self.adapter_agent.get_device(device.id) device.oper_status = OperStatus.ACTIVE self.adapter_agent.update_device(device) @inlineCallbacks def _initialize_onu(self, device): device = self.adapter_agent.get_device(device.id) self.adapter_agent.register_for_proxied_messages(device.proxy_address) log.debug("INIT", device=device) # DO things to the ONU # |###[ OmciFrame ]### # | transaction_id= 1 # | message_type= 79 # | omci = 10 # | \omci_message\ # | |###[ OmciMibReset ]### # | | entity_class= 2 # | | entity_id = 0 # | omci_trailer= 40 # OmciMibReset msg = OmciMibReset(entity_class=2, entity_id=0) frame = OmciFrame(transaction_id=self.trangen.next(), message_type=OmciMibReset.message_id, omci_message=msg) self.adapter_agent.send_proxied_message(device.proxy_address, frame) response = yield self.incoming_messages.get() if OmciMibResetResponse not in response: log.error("Failed to perform a MIB reset for {}".format( device.proxy_address)) return # ###[ PAS5211Dot3 ]### # dst = 00:0c:d5:00:01:00 # src = 90:e2:ba:82:f9:77 # len = 22 # ###[ PAS5211FrameHeader ]### # part = 1 # total_parts= 1 # size = 16 # magic_number= 0x1234abcd # ###[ PAS5211MsgHeader ]### # sequence_number= 51 # opcode = 0x3009 # event_type= 0 # channel_id= 0 # onu_id = 0 # onu_session_id= 1 # ###[ PAS5211GetOnuAllocs ]### # nothing = 0 # ###[ Raw ]### # load = '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' msg = PAS5211GetOnuAllocs() self.adapter_agent.send_proxied_message(device.proxy_address, msg) response = yield self.incoming_messages.get() if PAS5211GetOnuAllocsResponse not in response: log.error("Failed to get alloc ids for {}".format( device.proxy_address)) return # ###[ PAS5211Dot3 ]### # dst = 00:0c:d5:00:01:00 # src = 90:e2:ba:82:f9:77 # len = 30 # ###[ PAS5211FrameHeader ]### # part = 1 # total_parts= 1 # size = 24 # magic_number= 0x1234abcd # ###[ PAS5211MsgHeader ]### # sequence_number= 52 # opcode = 0x3007 # event_type= 0 # channel_id= 0 # onu_id = -1 # onu_session_id= -1 # ###[ PAS5211GetSnInfo ]### # serial_number= 'PMCS\xd5b\x84\xac' # ###[ Raw ]### # load = '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' msg = PAS5211GetSnInfo(serial_number=device.serial_number) self.adapter_agent.send_proxied_message(device.proxy_address, msg) response = yield self.incoming_messages.get() if PAS5211GetSnInfoResponse not in response: log.error("Failed to get serial number info for {}".format( device.proxy_address)) return # ###[ PAS5211Dot3 ]### # dst = 00:0c:d5:00:01:00 # src = 90:e2:ba:82:f9:77 # len = 22 # ###[ PAS5211FrameHeader ]### # part = 1 # total_parts= 1 # size = 16 # magic_number= 0x1234abcd # ###[ PAS5211MsgHeader ]### # sequence_number= 53 # opcode = 0x3074 # event_type= 0 # channel_id= 0 # onu_id = -1 # onu_session_id= -1 # ###[ PAS5211GetOnusRange ]### # nothing = 0 # ###[ Raw ]### # load = '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' msg = PAS5211GetOnusRange() self.adapter_agent.send_proxied_message(device.proxy_address, msg) response = yield self.incoming_messages.get() if PAS5211GetOnusRangeResponse not in response: log.error("Failed to get ONU Range for {}".format( device.proxy_address)) return # | ###[ OmciFrame ]### # | transaction_id = 2 # | message_type = 72 # | omci = 10 # | \omci_message \ # | | ###[ OmciSet ]### # | | entity_class = 262 # | | entity_id = 32769 # | | attributes_mask = 32768 # | | data = {'alloc_id': 1000} # | omci_trailer = 40 # OmciSet # TODO: maskdata msg = OmciSet(entity_class=262, entity_id=32769, attributes_mask=32768, data=dict(alloc_id=1000)) frame = OmciFrame(transaction_id=self.trangen.next(), message_type=OmciSet.message_id, omci_message=msg) self.adapter_agent.send_proxied_message(device.proxy_address, frame) response = yield self.incoming_messages.get() if OmciSetResponse not in response: log.error("Failed to set alloc id for {}".format( device.proxy_address)) return # length = 44 # port_type = 0 # port_id = 0 # management_frame = 1 # \frame \ # | ###[ OmciFrame ]### # | transaction_id = 3 # | message_type = 68 # | omci = 10 # | \omci_message \ # | | ###[ OmciCreate ]### # | | entity_class = 45 # | | entity_id = 1 # | | data = {'max_age': 5120, 'hello_time': 512, 'priority': 32768, 'port_bridging_ind': 0, # 'spanning_tree_ind': 0, 'unknown_mac_address_discard': 0, 'mac_learning_depth': 128, # 'learning_ind': 0, 'forward_delay': 3840} # | | ###[ Raw ]### # | | load = '\x00\x00\x00\n\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' # | omci_trailer = 40 # Found in method: pmc_omci_mac_bridge_sp_me_create from: PMC_OFAL.c # Params # - priority: The bridge priority set on the LAN card # - max_age: The maximum age for an entry in the spanning tree listing # - hello_time: The time interval between hello packets # - forward_delay: The time that the bridge on the Ethernet card in the ONT retains a packet before forwarding it # - unknown_mac_address_discard: frames with unknown destination addresses will be forwarded to all allowed ports msg = OmciCreate(entity_class=45, entity_id=1, data=dict(max_age=5120, hello_time=512, priority=32768, port_bridging_ind=PON_FALSE, spanning_tree_ind=PON_FALSE, unknown_mac_address_discard=0, mac_learning_depth=128, learning_ind=PON_FALSE, forward_delay=3840)) frame = OmciFrame(transaction_id=self.trangen.next(), message_type=OmciCreate.message_id, omci_message=msg) self.adapter_agent.send_proxied_message(device.proxy_address, frame) response = yield self.incoming_messages.get() if OmciCreateResponse not in response: log.error("Failed to set parameter on {}".format( device.proxy_address)) return # |###[ OmciFrame ]### # | transaction_id= 4 # | message_type= 68 # | omci = 10 # | \omci_message\ # | |###[ OmciCreate ]### # | | entity_class= 47 # | | entity_id = 0 # | | data = {'tp_pointer': 257, 'encapsulation_methods': 1, 'port_num': 0, 'port_priority': 10, 'tp_type': 1, 'port_path_cost': 100, 'port_spanning_tree_in': 0, 'lan_fcs_ind': 0, 'bridge_id_pointer': 1} # | omci_trailer= 40 # Found in method: pmc_omci_mac_bridge_pcd_me_create from: PMC_OFAL.c # Params # - port_path_cost: The cost contribution of the port to the path cost towards the spanning tree root bridge # - bridge_id_pointer: MAC bridge controlling the port msg = OmciCreate( entity_class=47, entity_id=0, data=dict( tp_pointer=257, encapsulation_methods=OMCI_MAC_BRIDGE_PCD_ENCAP_METHOD_LLC, port_num=0, port_priority=10, tp_type=1, port_path_cost=100, port_spanning_tree_in=PON_FALSE, lan_fcs_ind=OMCI_MAC_BRIDGE_PCD_LANFCS_FORWARDED, bridge_id_pointer=1)) frame = OmciFrame(transaction_id=self.trangen.next(), message_type=OmciCreate.message_id, omci_message=msg) self.adapter_agent.send_proxied_message(device.proxy_address, frame) response = yield self.incoming_messages.get() if OmciCreateResponse not in response: log.error("Failed to set info for {}".format(device.proxy_address)) return # |###[ OmciFrame ]### # | transaction_id= 5 # | message_type= 68 # | omci = 10 # | \omci_message\ # | |###[ OmciCreate ]### # | | entity_class= 171 # | | entity_id = 0 # | | data = {'association_type': 2, 'associated_me_pointer': 257} # | omci_trailer= 40 # Found in method: pmc_omci_evto_create from: PMC_OFAL.c msg = OmciCreate( entity_class=171, entity_id=0, data=dict(association_type= OMCI_EX_VLAN_TAG_OCD_ASSOCIATION_TYPE_PPTP_ETH_UNI, associated_me_pointer=257)) frame = OmciFrame(transaction_id=self.trangen.next(), message_type=OmciCreate.message_id, omci_message=msg) self.adapter_agent.send_proxied_message(device.proxy_address, frame) response = yield self.incoming_messages.get() if OmciCreateResponse not in response: log.error("Failed to set association info for {}".format( device.proxy_address)) return # |###[ OmciFrame ]### # | transaction_id= 6 # | message_type= 72 # | omci = 10 # | \omci_message\ # | |###[ OmciSet ]### # | | entity_class= 171 # | | entity_id = 0 # | | attributes_mask= 47616 # | | data = {'association_type': 2, 'input_tpid': 33024, 'associated_me_pointer': 257, 'downstream_mode': 0, 'output_tpid': 33024} # | omci_trailer= 40 # Found in method: pmc_omci_evto_set from: PMC_OFAL.c msg = OmciSet( entity_class=171, entity_id=0, attributes_mask=47616, data=dict(association_type= OMCI_EX_VLAN_TAG_OCD_ASSOCIATION_TYPE_PPTP_ETH_UNI, input_tpid=33024, associated_me_pointer=257, downstream_mode=OMCI_EX_VLAN_TAG_OCD_DS_MODE_US_INVERSE, output_tpid=33024)) frame = OmciFrame(transaction_id=self.trangen.next(), message_type=OmciSet.message_id, omci_message=msg) self.adapter_agent.send_proxied_message(device.proxy_address, frame) response = yield self.incoming_messages.get() if OmciSetResponse not in response: log.error("Failed to set association tpid info for {}".format( device.proxy_address)) return # |###[ OmciFrame ]### # | transaction_id= 7 # | message_type= 68 # | omci = 10 # | \omci_message\ # | |###[ OmciCreate ]### # | | entity_class= 130 # | | entity_id = 1 # | | data = {'tp_pointer': 65535, 'unmarked_frame_option': 1, 'interwork_tp_pointer_for_p_bit_priority_6': 65535, # 'interwork_tp_pointer_for_p_bit_priority_7': 65535, 'interwork_tp_pointer_for_p_bit_priority_4': 65535, # 'interwork_tp_pointer_for_p_bit_priority_5': 65535, 'interwork_tp_pointer_for_p_bit_priority_2': 65535, # 'interwork_tp_pointer_for_p_bit_priority_3': 65535, 'interwork_tp_pointer_for_p_bit_priority_0': 65535, # 'interwork_tp_pointer_for_p_bit_priority_1': 65535, 'tp_type': 0, 'default_p_bit_marking': 0} # | omci_trailer= 40 # Found in method: pmc_omci_8021p_msp_me_create from: PMC_OFAL.c msg = OmciCreate( entity_class=130, entity_id=1, data=dict( tp_pointer=65535, unmarked_frame_option=OMCI_8021P_MSP_UNMARKED_FRAME_TAG_FRAME, interwork_tp_pointer_for_p_bit_priority_6=65535, interwork_tp_pointer_for_p_bit_priority_7=65535, interwork_tp_pointer_for_p_bit_priority_4=65535, interwork_tp_pointer_for_p_bit_priority_5=65535, interwork_tp_pointer_for_p_bit_priority_2=65535, interwork_tp_pointer_for_p_bit_priority_3=65535, interwork_tp_pointer_for_p_bit_priority_0=65535, interwork_tp_pointer_for_p_bit_priority_1=65535, tp_type=OMCI_8021P_MSP_TP_TYPE_NULL, default_p_bit_marking=0)) frame = OmciFrame(transaction_id=self.trangen.next(), message_type=OmciCreate.message_id, omci_message=msg) self.adapter_agent.send_proxied_message(device.proxy_address, frame) response = yield self.incoming_messages.get() if OmciCreateResponse not in response: log.error("Failed to set interwork info for {}".format( device.proxy_address)) return # |###[ OmciFrame ]### # | transaction_id= 8 # | message_type= 68 # | omci = 10 # | \omci_message\ # | |###[ OmciCreate ]### # | | entity_class= 130 # | | entity_id = 1 # | | data = {'tp_pointer': 1, 'encapsulation_methods': 1, 'port_num': 1, 'port_priority': 3, 'tp_type': 5, 'port_path_cost': 32, 'port_spanning_tree_in': 1, 'lan_fcs_ind': 0, 'bridge_id_pointer': 1} # | omci_trailer= 40 # Found in method: pmc_omci_mac_bridge_pcd_me_create from: PMC_OFAL.c # Params # - port_path_cost: The cost contribution of the port to the path cost towards the spanning tree root bridge # - bridge_id_pointer: MAC bridge controlling the port msg = OmciCreate( entity_class=130, entity_id=1, data=dict( tp_pointer=1, encapsulation_methods=OMCI_MAC_BRIDGE_PCD_ENCAP_METHOD_LLC, port_num=1, port_priority=3, tp_type=5, port_path_cost=32, port_spanning_tree_in=PON_TRUE, lan_fcs_ind=OMCI_MAC_BRIDGE_PCD_LANFCS_FORWARDED, bridge_id_pointer=1)) frame = OmciFrame(transaction_id=self.trangen.next(), message_type=OmciCreate.message_id, omci_message=msg) self.adapter_agent.send_proxied_message(self, device.proxy_address, frame) response = yield self.incoming_messages.get() if OmciCreateResponse not in response: log.error("Failed to set encap info for {}".format( device.proxy_address)) return # |###[ OmciFrame ]### # | transaction_id= 9 # | message_type= 68 # | omci = 10 # | \omci_message\ # | |###[ OmciCreate ]### # | | entity_class= 268 # | | entity_id = 1 # | | data = {'priority_queue_pointer_downstream': 0, 'direction': 3, 'tcont_pointer': 32769, 'traffic_descriptor_profile_pointer': 0, 'traffic_management_pointer_upstream': 4, 'port_id': 1000} # | omci_trailer= 40 # Found in method: pmc_omci_gem_nctp_create from: PMC_OFAL.c msg = OmciCreate(entity_class=268, entity_id=1, data=dict(priority_queue_pointer_downstream=0, direction=GEM_DIR_BIDIRECT, tcont_pointer=32769, traffic_descriptor_profile_pointer=0, traffic_management_pointer_upstream=4, port_id=1000)) frame = OmciFrame(transaction_id=self.trangen.next(), message_type=OmciCreate.message_id, omci_message=msg) self.adapter_agent.send_proxied_message(device.proxy_address, frame) response = yield self.incoming_messages.get() if OmciCreateResponse not in response: log.error("Failed to priority queue for {}".format( device.proxy_address)) return # |###[ OmciFrame ]### # | transaction_id= 10 # | message_type= 68 # | omci = 10 # | \omci_message\ # | |###[ OmciCreate ]### # | | entity_class= 266 # | | entity_id = 1 # | | data = {'gem_port_network_ctp_pointer': 1, 'gal_profile_pointer': 0, 'service_profile_pointer': 1, 'interworking_option': 5, 'interworking_tp_pointer': 0} # | omci_trailer= 40 # Found in method: pmc_omci_gem_iwtp_me_create from: PMC_OFAL.c # Params # - gem_port_network_ctp_pointer: An instance identifier of the GEM Port Network CTP that is associated with this GEM Interworking Termination Point # - service_profile_pointer: The service profile type and a pointer to the instance of a service profile # - interworking_tp_pointer: Used for in the case of Circuit Emulation Services and 802.1p mapper service # - gal_profile_pointer: A pointer to an instance of the GAL Profile msg = OmciCreate( entity_class=266, entity_id=1, data=dict(gem_port_network_ctp_pointer=1, gal_profile_pointer=0, service_profile_pointer=1, interworking_option=OMCI_GEM_IWTP_IW_OPT_8021P_MAPPER, interworking_tp_pointer=0)) frame = OmciFrame(transaction_id=self.trangen.next(), message_type=OmciCreate.message_id, omci_message=msg) self.adapter_agent.send_proxied_message(device.proxy_address, frame) response = yield self.incoming_messages.get() if OmciCreateResponse not in response: log.error("Failed to set gem info for {}".format( device.proxy_address)) return
class SimulatedOltAdapter(object): name = 'simulated_olt' supported_device_types = [ DeviceType(id='simulated_olt', adapter=name, accepts_bulk_flow_update=True) ] app = Klein() def __init__(self, adapter_agent, config): self.adapter_agent = adapter_agent self.config = config self.descriptor = Adapter( id=self.name, vendor='Voltha project', version='0.1', config=AdapterConfig(log_level=LogLevel.INFO)) self.control_endpoint = None # Faked PM metrics for testing PM functionality self.pm_metrics = None def start(self): log.debug('starting') # setup a basic web server for test control self.control_endpoint = endpoints.TCP4ServerEndpoint(reactor, 18880) self.control_endpoint.listen(self.get_test_control_site()) # TODO tmp: populate some devices and logical devices # reactor.callLater(0, self._tmp_populate_stuff) log.info('started') def stop(self): log.debug('stopping') log.info('stopped') def adapter_descriptor(self): return self.descriptor def device_types(self): return DeviceTypes(items=self.supported_device_types) def health(self): return HealthStatus(state=HealthStatus.HealthState.HEALTHY) def change_master_state(self, master): raise NotImplementedError() def adopt_device(self, device): # We kick of a simulated activation scenario reactor.callLater(0.2, self._simulate_device_activation, device) return device def reconcile_device(self, device): raise NotImplementedError() def abandon_device(self, device): raise NotImplementedError() def disable_device(self, device): raise NotImplementedError() def reenable_device(self, device): raise NotImplementedError() def reboot_device(self, device): raise NotImplementedError() def delete_device(self, device): raise NotImplementedError() def download_image(self, device, request): log.info('download-image', device=device, request=request) try: # initiate requesting software download to device log.info('device.image_downloads', img_dnld=device.image_downloads) pass except Exception as e: log.exception(e.message) def get_image_download_status(self, device, request): log.info('get-image-download-status', device=device,\ request=request) try: download_completed = False # initiate query for progress of download to device request.state = random.choice([ ImageDownload.DOWNLOAD_SUCCEEDED, ImageDownload.DOWNLOAD_STARTED, ImageDownload.DOWNLOAD_FAILED ]) if request.state != ImageDownload.DOWNLOAD_STARTED: download_completed = True request.downloaded_bytes = random.choice(range(1024, 65536)) # update status based on query output self.adapter_agent.update_image_download(request) if download_completed == True: # restore admin state to enabled device.admin_state = AdminState.ENABLED self.adapter_agent.update_device(device) # TODO: # the device admin state will also restore # when adapter receiving event notification # this will be handled in event handler except Exception as e: log.exception(e.message) def cancel_image_download(self, device, request): log.info('cancel-sw-download', device=device, request=request) try: # intiate cancelling software download to device # at success delete image download record self.adapter_agent.delete_image_download(request) # restore admin state to enabled device.admin_state = AdminState.ENABLED self.adapter_agent.update_device(device) except Exception as e: log.exception(e.message) def activate_image_update(self, device, request): log.info('activate-image-update', device=device, request=request) try: # initiate activating software update to device # at succcess, update image state request.image_state = ImageDownload.IMAGE_ACTIVE self.adapter_agent.update_image_download(request) # restore admin state to enabled device.admin_state = AdminState.ENABLED self.adapter_agent.update_device(device) except Exception as e: log.exception(e.message) def revert_image_update(self, device, request): log.info('revert-image-updade', device=device, request=request) try: # initiate reverting software update to device # at succcess, update image state request.image_state = ImageDownload.IMAGE_INACTIVE self.adapter_agent.update_image_download(request) # restore admin state to enabled device.admin_state = AdminState.ENABLED self.adapter_agent.update_device(device) except Exception as e: log.exception(e.message) def get_device_details(self, device): raise NotImplementedError() def update_pm_config(self, device, pm_config): log.info("adapter-update-pm-config", device=device, pm_config=pm_config) self.pm_metrics.update(pm_config) def self_test_device(self, device): log.info("run-self-test-on-device", device=device.id) result = SelfTestResponse(result=random.choice([ SelfTestResponse.SUCCESS, SelfTestResponse.FAILURE, SelfTestResponse.NOT_SUPPORTED, SelfTestResponse.UNKNOWN_ERROR ])) return result def _tmp_populate_stuff(self): """ pretend that we discovered some devices and create: - devices - device ports for each - logical device - logical device ports """ olt = Device(id='simulated_olt_1', type='simulated_olt', root=True, vendor='simulated', model='n/a', hardware_version='n/a', firmware_version='n/a', software_version='1.0', serial_number=uuid4().hex, adapter=self.name, oper_status=OperStatus.DISCOVERED) self.adapter_agent.add_device(olt) self.adapter_agent.add_port( olt.id, Port(port_no=1, label='pon', type=Port.PON_OLT)) self.adapter_agent.add_port( olt.id, Port(port_no=2, label='eth', type=Port.ETHERNET_NNI)) onu1 = Device(id='simulated_onu_1', type='simulated_onu', root=False, parent_id=olt.id, parent_port_no=1, vendor='simulated', model='n/a', hardware_version='n/a', firmware_version='n/a', software_version='1.0', serial_number=uuid4().hex, adapter='simulated_onu', oper_status=OperStatus.DISCOVERED, vlan=101) self.adapter_agent.add_device(onu1) self.adapter_agent.add_port( onu1.id, Port(port_no=2, label='eth', type=Port.ETHERNET_UNI)) self.adapter_agent.add_port( onu1.id, Port(port_no=1, label='pon', type=Port.PON_ONU, peers=[Port.PeerPort(device_id=olt.id, port_no=1)])) onu2 = Device(id='simulated_onu_2', type='simulated_onu', root=False, parent_id=olt.id, parent_port_no=1, vendor='simulated', model='n/a', hardware_version='n/a', firmware_version='n/a', software_version='1.0', serial_number=uuid4().hex, adapter='simulated_onu', oper_status=OperStatus.DISCOVERED, vlan=102) self.adapter_agent.add_device(onu2) self.adapter_agent.add_port( onu2.id, Port(port_no=2, label='eth', type=Port.ETHERNET_UNI)) self.adapter_agent.add_port( onu2.id, Port(port_no=1, label='pon', type=Port.PON_ONU, peers=[Port.PeerPort(device_id=olt.id, port_no=1)])) ld = LogicalDevice( id='simulated1', datapath_id=1, desc=ofp_desc(mfr_desc='cord project', hw_desc='simualted pon', sw_desc='simualted pon', serial_num=uuid4().hex, dp_desc='n/a'), switch_features=ofp_switch_features( n_buffers=256, # TODO fake for now n_tables=2, # TODO ditto capabilities=( # TODO and ditto OFPC_FLOW_STATS | OFPC_TABLE_STATS | OFPC_PORT_STATS | OFPC_GROUP_STATS)), root_device_id=olt.id) self.adapter_agent.create_logical_device(ld) cap = OFPPF_1GB_FD | OFPPF_FIBER for port_no, name, device_id, device_port_no, root_port in [ (1, 'onu1', onu1.id, 2, False), (2, 'onu2', onu2.id, 2, False), (129, 'olt1', olt.id, 2, True) ]: port = LogicalPort(id=name, ofp_port=ofp_port( port_no=port_no, hw_addr=mac_str_to_tuple( '00:00:00:00:00:%02x' % port_no), name=name, config=0, state=OFPPS_LIVE, curr=cap, advertised=cap, peer=cap, curr_speed=OFPPF_1GB_FD, max_speed=OFPPF_1GB_FD), device_id=device_id, device_port_no=device_port_no, root_port=root_port) self.adapter_agent.add_logical_port(ld.id, port) olt.parent_id = ld.id self.adapter_agent.update_device(olt) @inlineCallbacks def _simulate_device_activation(self, device): # first we pretend that we were able to contact the device and obtain # additional information about it device.root = True device.vendor = 'simulated' device.model = 'n/a' device.hardware_version = 'n/a' device.firmware_version = 'n/a' device.serial_number = uuid4().hex device.connect_status = ConnectStatus.REACHABLE image1 = Image(name="olt_candidate1", version="1.0", hash="", install_datetime=datetime.datetime.utcnow().isoformat(), is_active=True, is_committed=True, is_valid=True) image2 = Image(name="olt_candidate2", version="1.0", hash="", install_datetime=datetime.datetime.utcnow().isoformat(), is_active=False, is_committed=False, is_valid=True) device.images.image.extend([image1, image2]) self.adapter_agent.update_device(device) # Now set the initial PM configuration for this device self.pm_metrics = AdapterPmMetrics(device) pm_config = self.pm_metrics.make_proto() log.info("initial-pm-config", pm_config=pm_config) self.adapter_agent.update_device_pm_config(pm_config, init=True) # then shortly after we create some ports for the device yield asleep(0.05) nni_port = Port(port_no=2, label='NNI facing Ethernet port', type=Port.ETHERNET_NNI, admin_state=AdminState.ENABLED, oper_status=OperStatus.ACTIVE) self.adapter_agent.add_port(device.id, nni_port) self.adapter_agent.add_port( device.id, Port(port_no=1, label='PON port', type=Port.PON_OLT, admin_state=AdminState.ENABLED, oper_status=OperStatus.ACTIVE)) # then shortly after we create the logical device with one port # that will correspond to the NNI port yield asleep(0.05) logical_device_id = uuid4().hex[:12] ld = LogicalDevice( # not setting id and datapth_id will let the adapter agent pick id desc=ofp_desc(mfr_desc='cord porject', hw_desc='simualted pon', sw_desc='simualted pon', serial_num=uuid4().hex, dp_desc='n/a'), switch_features=ofp_switch_features( n_buffers=256, # TODO fake for now n_tables=2, # TODO ditto capabilities=( # TODO and ditto OFPC_FLOW_STATS | OFPC_TABLE_STATS | OFPC_PORT_STATS | OFPC_GROUP_STATS)), root_device_id=device.id) ld_initialized = self.adapter_agent.create_logical_device(ld) cap = OFPPF_1GB_FD | OFPPF_FIBER self.adapter_agent.add_logical_port( ld_initialized.id, LogicalPort(id='nni', ofp_port=ofp_port(port_no=129, hw_addr=mac_str_to_tuple( '00:00:00:00:00:%02x' % 129), name='nni', config=0, state=OFPPS_LIVE, curr=cap, advertised=cap, peer=cap, curr_speed=OFPPF_1GB_FD, max_speed=OFPPF_1GB_FD), device_id=device.id, device_port_no=nni_port.port_no, root_port=True)) # and finally update to active device = self.adapter_agent.get_device(device.id) device.parent_id = ld_initialized.id device.oper_status = OperStatus.ACTIVE self.adapter_agent.update_device(device) reactor.callLater(0.1, self._simulate_detection_of_onus, device.id) self.start_kpi_collection(device.id) self.start_alarm_simulation(device.id) @inlineCallbacks def _simulate_detection_of_onus(self, device_id): try: for i in xrange(1, 5): log.info('activate-olt-for-onu-{}'.format(i)) vlan_id = self._olt_side_onu_activation(i) yield asleep(0.05) self.adapter_agent.child_device_detected( parent_device_id=device_id, parent_port_no=1, child_device_type='simulated_onu', proxy_address=Device.ProxyAddress(device_id=device_id, channel_id=vlan_id), admin_state=AdminState.ENABLED, vlan=vlan_id) except Exception as e: log.exception('error', e=e) def _olt_side_onu_activation(self, seq): """ This is where if this was a real OLT, the OLT-side activation for the new ONU should be performed. By the time we return, the OLT shall be able to provide tunneled (proxy) communication to the given ONU, using the returned information. """ vlan_id = seq + 100 return vlan_id #def update_pm_collection(self, device, pm_collection_config): # This is where the metrics to be collected are configured and where # the sampling frequency is set. #TODO: Here. # pass def update_flows_bulk(self, device, flows, groups): log.debug('bulk-flow-update', device_id=device.id, flows=flows, groups=groups) # sample code that analyzes the incoming flow table assert len(groups.items) == 0, "Cannot yet deal with groups" for flow in flows.items: in_port = get_in_port(flow) assert in_port is not None if in_port == 2: # Downstream rule for field in get_ofb_fields(flow): if field.type == ETH_TYPE: _type = field.eth_type pass # construct ether type based condition here elif field.type == IP_PROTO: _proto = field.ip_proto pass # construct ip_proto based condition here elif field.type == IN_PORT: _port = field.port pass # construct in_port based condition here elif field.type == VLAN_VID: _vlan_vid = field.vlan_vid pass # construct VLAN ID based filter condition here elif field.type == VLAN_PCP: _vlan_pcp = field.vlan_pcp pass # construct VLAN PCP based filter condition here elif field.type == METADATA: pass # safe to ignore # TODO else: raise NotImplementedError('field.type={}'.format( field.type)) for action in get_actions(flow): if action.type == OUTPUT: pass # construct packet emit rule here elif action.type == PUSH_VLAN: if action.push.ethertype != 0x8100: log.error('unhandled-ether-type', ethertype=action.push.ethertype) pass # construct vlan push command here elif action.type == POP_VLAN: pass # construct vlan pop command here elif action.type == SET_FIELD: assert (action.set_field.field.oxm_class == ofp.OFPXMC_OPENFLOW_BASIC) field = action.set_field.field.ofb_field if field.type == VLAN_VID: pass # construct vlan_id set command here else: log.error('unsupported-action-set-field-type', field_type=field.type) else: log.error('unsupported-action-type', action_type=action.type) # final assembly of low level device flow rule and pushing it # down to device pass elif in_port == 1: # Upstream rule for field in get_ofb_fields(flow): if field.type == ETH_TYPE: _type = field.eth_type pass # construct ether type based condition here elif field.type == IP_PROTO: _proto = field.ip_proto pass # construct ip_proto based condition here elif field.type == IN_PORT: _port = field.port pass # construct in_port based condition here elif field.type == VLAN_VID: _vlan_vid = field.vlan_vid pass # construct VLAN ID based filter condition here elif field.type == VLAN_PCP: _vlan_pcp = field.vlan_pcp pass # construct VLAN PCP based filter condition here elif field.type == UDP_SRC: _udp_src = field.udp_src pass # construct UDP SRC based filter here elif field.type == UDP_DST: _udp_dst = field.udp_dst pass # construct UDP DST based filter here # TODO else: raise NotImplementedError('field.type={}'.format( field.type)) for action in get_actions(flow): if action.type == OUTPUT: pass # construct packet emit rule here elif action.type == PUSH_VLAN: if action.push.ethertype != 0x8100: log.error('unhandled-ether-type', ethertype=action.push.ethertype) pass # construct vlan push command here elif action.type == SET_FIELD: assert (action.set_field.field.oxm_class == ofp.OFPXMC_OPENFLOW_BASIC) field = action.set_field.field.ofb_field if field.type == VLAN_VID: pass # construct vlan_id set command here else: log.error('unsupported-action-set-field-type', field_type=field.type) else: log.error('unsupported-action-type', action_type=action.type) # final assembly of low level device flow rule and pushing it # down to device pass else: raise Exception('Port should be 1 or 2 by our convention') def update_flows_incrementally(self, device, flow_changes, group_changes): raise NotImplementedError() def send_proxied_message(self, proxy_address, msg): log.info('send-proxied-message', proxy_address=proxy_address, msg=msg) # we mimic a response by sending the same message back in a short time reactor.callLater(0.2, self.adapter_agent.receive_proxied_message, proxy_address, msg) def receive_proxied_message(self, proxy_address, msg): raise NotImplementedError() def receive_packet_out(self, logical_device_id, egress_port_no, msg): log.info('packet-out', logical_device_id=logical_device_id, egress_port_no=egress_port_no, msg_len=len(msg)) def receive_inter_adapter_message(self, msg): raise NotImplementedError() def suppress_alarm(self, filter): raise NotImplementedError() def unsuppress_alarm(self, filter): raise NotImplementedError() def start_kpi_collection(self, device_id): """Simulate periodic KPI metric collection from the device""" import random @inlineCallbacks # pretend that we need to do async calls def _collect(device_id, prefix): try: # Step 1: gather metrics from device (pretend it here) - examples # upgraded the metrics to include packet statistics for # testing. nni_port_metrics = self.pm_metrics.collect_nni_metrics() pon_port_metrics = self.pm_metrics.collect_pon_metrics() olt_metrics = yield dict(cpu_util=20 + 5 * random.random(), buffer_util=10 + 10 * random.random()) # 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={ # OLT-level prefix: MetricValuePairs(metrics=olt_metrics), # OLT NNI port prefix + '.nni': MetricValuePairs(metrics=nni_port_metrics), # OLT PON port prefix + '.pon': MetricValuePairs(metrics=pon_port_metrics) }) # Step 3: submit self.adapter_agent.submit_kpis(kpi_event) except Exception as e: log.exception('failed-to-submit-kpis', e=e) self.pm_metrics.start_collector(self.name, device_id, _collect) #prefix = 'voltha.{}.{}'.format(self.name, device_id) #lc = LoopingCall(_collect, device_id, prefix) #lc.start(interval=15) # TODO make this configurable def start_alarm_simulation(self, device_id): """Simulate periodic device alarms""" import random def _generate_alarm(device_id): try: # Randomly choose values for each enum types severity = random.choice( list(v for k, v in AlarmEventSeverity.DESCRIPTOR. enum_values_by_name.items())) state = random.choice( list(v for k, v in AlarmEventState.DESCRIPTOR. enum_values_by_name.items())) type = random.choice( list( v for k, v in AlarmEventType.DESCRIPTOR.enum_values_by_name.items())) category = random.choice( list(v for k, v in AlarmEventCategory.DESCRIPTOR. enum_values_by_name.items())) description = "Simulated alarm - " \ "device:{} " \ "type:{} " \ "severity:{} " \ "state:{} " \ "category:{}".format(device_id, type.name, severity.name, state.name, category.name) current_context = {} for key, value in self.__dict__.items(): current_context[key] = str(value) alarm_event = self.adapter_agent.create_alarm( resource_id=device_id, type=type.number, category=category.number, severity=severity.number, state=state.number, description=description, context=current_context) self.adapter_agent.submit_alarm(device_id, alarm_event) except Exception as e: log.exception('failed-to-submit-alarm', e=e) alarm_lc = LoopingCall(_generate_alarm, device_id) alarm_lc.start(30) def create_interface(self, device, data): raise NotImplementedError() def update_interface(self, device, data): raise NotImplementedError() def remove_interface(self, device, data): raise NotImplementedError() def receive_onu_detect_state(self, device_id, state): raise NotImplementedError() def create_tcont(self, device, tcont_data, traffic_descriptor_data): raise NotImplementedError() def update_tcont(self, device, tcont_data, traffic_descriptor_data): raise NotImplementedError() def remove_tcont(self, device, tcont_data, traffic_descriptor_data): raise NotImplementedError() def create_gemport(self, device, data): raise NotImplementedError() def update_gemport(self, device, data): raise NotImplementedError() def remove_gemport(self, device, data): raise NotImplementedError() def create_multicast_gemport(self, device, data): raise NotImplementedError() def update_multicast_gemport(self, device, data): raise NotImplementedError() def remove_multicast_gemport(self, device, data): raise NotImplementedError() def create_multicast_distribution_set(self, device, data): raise NotImplementedError() def update_multicast_distribution_set(self, device, data): raise NotImplementedError() def remove_multicast_distribution_set(self, device, data): raise NotImplementedError() # ~~~~~~~~~~~~~~~~~~~~ Embedded test Klein rest server ~~~~~~~~~~~~~~~~~~~~ def get_test_control_site(self): return Site(self.app.resource()) @app.route('/devices/<string:device_id>/detect_onus') def detect_onus(self, request, device_id): log.info('detect-onus', request=request, device_id=device_id) self._simulate_detection_of_onus(device_id) return '{"status": "OK"}' @app.route('/devices/<string:device_id>/test_eapol_in') def test_eapol_in(self, request, device_id): """Simulate a packet in message posted upstream""" log.info('test_eapol_in', request=request, device_id=device_id) eapol_start = str( Ether(src='00:11:22:33:44:55') / EAPOL(type=1, len=0) / Padding(load=42 * '\x00')) device = self.adapter_agent.get_device(device_id) self.adapter_agent.send_packet_in(logical_device_id=device.parent_id, logical_port_no=1, packet=eapol_start) return '{"status": "sent"}'
class RubyAdapterHandler(object): name = "microsemi_olt" supported_device_types = [ DeviceType(id=name, adapter=name, accepts_bulk_flow_update=True) ] def __init__(self, adaptor_agent, config, descriptor): self.adaptor_agent = adaptor_agent self.config = config self.descriptor = descriptor self.device = None self.device_manager = None self.comm = None self.activation = None self.olt = None self.ports = dict() self.last_iteration_ports = [] self.interface = registry('main').get_args().interface self.flow_queue = DeferredQueue() def stop(self): log.debug('stopping') self._abandon(self.target) log.info('stopped') return self def activate(self, device): log.debug('activate-device', device=device) self.last_iteration_ports = [] self.device = device self.device_manager = DeviceManager(device, self.adaptor_agent) self.target = device.mac_address self.comm = PAS5211Communication(dst_mac=self.target, iface=self.interface) self.olt = OltStateMachine(iface=self.interface, comm=self.comm, target=self.target, device=self.device_manager) self.activation = ActivationWatcher(iface=self.interface, comm=self.comm, target=self.target, device=self.device_manager, olt_adapter=self) reactor.callLater(0, self.wait_for_flow_events, device) self.olt.runbg() self.activation.runbg() def reboot_device(self, device): try: log.debug('reboot-device', device=device) # Stop ONUS ... self.device_manager.update_child_devices_state( admin_state=AdminState.DISABLED) # ... and then delete them! self.device_manager.delete_all_child_devices # Wait 10s to reboot OLT reactor.callLater(10, self.reboot_olt, device) except Exception as e: log.exception('reboot-olt-exception', e=e) def reboot_olt(self, device): try: # Stop OLT... self.device_manager.delete_logical_device() self.olt.stop() self.activation.stop() # ... and start again! ONUS will activate from events got from OLT self.last_iteration_ports = [] self.ports.clear() self.activate(device) except Exception as e: log.exception('reboot-olt-exception', e=e) def abandon_device(self, device): self._abandon(self.target) def get_port_list(self, flows): port_list = [] for flow in flows: _in_port = fd.get_in_port(flow) if _in_port not in (0, PMC_UPSTREAM_PORT): if _in_port not in port_list: port_list.append(_in_port) log.debug('field-type-in-port', in_port=_in_port, port_list=port_list) return port_list def get_svlan(self, port, flows): svlan_id = None for flow in flows: _in_port = fd.get_in_port(flow) if _in_port == PMC_UPSTREAM_PORT: log.debug('svlan-port-match') metadata = fd.get_metadata(flow) if metadata: if metadata == port: svlan_id = self.get_vlan(flow) & 0xfff log.debug('SVLAN found:{}'.format(svlan_id)) return svlan_id def get_cvlan(self, svlan_id, port, flows): cvlan_id = None # Look for cvlan ... for flow in flows: _in_port = fd.get_in_port(flow) if _in_port == port: log.debug('cvlan-port-match') for action in fd.get_actions(flow): if action.type == fd.SET_FIELD: vlan = action.set_field.field.ofb_field.vlan_vid & 0xfff if vlan == svlan_id: cvlan_id = self.get_vlan(flow) & 0xfff log.debug('CVLAN found:{}'.format(cvlan_id)) return cvlan_id def get_uplink_bandwidth(self, cvlan_id, svlan_id, port, flows): bandwidth = None # Look for cvlan ... for flow in flows: _in_port = fd.get_in_port(flow) if _in_port == port: log.debug('uplink-bandwidth-port-match') for action in fd.get_actions(flow): if action.type == fd.SET_FIELD: vlan = action.set_field.field.ofb_field.vlan_vid & 0xfff if vlan == svlan_id: bandwidth = fd.get_metadata(flow) if bandwidth: log.debug( 'Bandwidth found:{}'.format(bandwidth)) return bandwidth def get_downlink_bandwidth(self, cvlan_id, svlan_id, port, flows): return None def update_flow_table(self, device, flows): try: self.flow_queue.put({'flows': flows}) except Exception as e: log.debug('flow-enqueue-exception', e=e) @inlineCallbacks def wait_for_flow_events(self, device): log.debug('wait-for-flow-events', device=device) event = yield self.flow_queue.get() flows = event.get('flows') try: cvlan_id = None svlan_id = None log.debug('wait-for-flow-events-flow', device=device) # Look for ports mentioned in flows received ... port_list = self.get_port_list(flows.items) log.debug("list-ports", port_list=port_list) new_ports = set(port_list) - set(self.last_iteration_ports) log.debug("new-ports", new_ports=new_ports) disconnected_ports = set( self.last_iteration_ports) - set(port_list) log.debug("disconnected-ports", disconnected_ports=disconnected_ports) # For those new ports, check if we can proceed with flow installation... for port in new_ports: # Got svlan for that port ... svlan_id = self.get_svlan(port, flows.items) # ... look for the corresponding cvlan... if svlan_id: cvlan_id = self.get_cvlan(svlan_id, port, flows.items) # Both vlan found! if svlan_id and cvlan_id: # Get bandwidths from flow info... uplink_bandwidth = self.get_uplink_bandwidth( cvlan_id, svlan_id, port, flows.items) if uplink_bandwidth == None: uplink_bandwidth = SLA_be_bw_gros downlink_bandwidth = self.get_downlink_bandwidth( cvlan_id, svlan_id, port, flows.items) if downlink_bandwidth == None: if uplink_bandwidth == None: downlink_bandwidth = SLA_be_bw_gros else: downlink_bandwidth = uplink_bandwidth onu_id = self.ports[port]['onu_id'] onu_session_id = self.ports[port]['onu_session_id'] port_id = 1000 + 16 * onu_id alloc_id = port_id channel_id = port / 32 # Check if flow is already installed, if so, continue with next port if self.ports[port].get('cvlan') and self.ports[port].get( 'svlan'): if self.ports[port].get('svlan') == svlan_id: # Flow already installed if self.ports[port].get('cvlan') == cvlan_id: continue # We have new VLANs so we reinstall! else: self.reinstall_flows_sequence( device, onu_id, svlan_id, cvlan_id, port_id, alloc_id, onu_session_id, channel_id, uplink_bandwidth, downlink_bandwidth) else: # New installation... self.install_flows_sequence( device, onu_id, svlan_id, cvlan_id, port_id, alloc_id, onu_session_id, channel_id, uplink_bandwidth, downlink_bandwidth) else: # New installation... self.install_flows_sequence(device, onu_id, svlan_id, cvlan_id, port_id, alloc_id, onu_session_id, channel_id, uplink_bandwidth, downlink_bandwidth) self.ports[port]['svlan'] = svlan_id self.ports[port]['cvlan'] = cvlan_id else: # Finally, it is an incomplete port, so we remove from port list try: port_list.remove(port) except Exception as e: log.debug('remove-non-existing-port', e=e) # For those ports without flows, uninstall them for port in disconnected_ports: onu_id = self.ports[port]['onu_id'] onu_session_id = self.ports[port]['onu_session_id'] port_id = 1000 + 16 * onu_id alloc_id = port_id channel_id = port / 32 if self.ports[port].get('cvlan') and self.ports[port].get( 'svlan'): self.uninstall_flows_sequence(device, onu_id, port_id, alloc_id, onu_session_id, channel_id) self.ports[port]['svlan'] = None self.ports[port]['cvlan'] = None self.last_iteration_ports = port_list log.debug('last-iteration-ports', ports=self.last_iteration_ports) except Exception as e: log.exception('failed-to-olt-update-flow-table', e=e) reactor.callLater(0, self.wait_for_flow_events, device) def get_vlan(self, flow): for field in fd.get_ofb_fields(flow): if field.type == fd.VLAN_VID: return field.vlan_vid return None def reinstall_flows_sequence(self, device, onu_id, svlan, cvlan, port_id, alloc_id, onu_session_id, channel_id, uplink_bandwidth, downlink_bandwidth): log.debug('init-flow-reinstallaton') try: olt = OltReinstallFlowStateMachine( iface=self.interface, comm=self.comm, target=self.target, device=self.device_manager, onu_id=onu_id, channel_id=channel_id, port_id=port_id, onu_session_id=onu_session_id, alloc_id=alloc_id, svlan_id=svlan, cvlan_id=cvlan, uplink_bandwidth=uplink_bandwidth, downlink_bandwidth=downlink_bandwidth) olt.runbg() except Exception as e: log.exception('failed-to-launch-reinstall-flow', e=e) def install_flows_sequence(self, device, onu_id, svlan, cvlan, port_id, alloc_id, onu_session_id, channel_id, uplink_bandwidth, downlink_bandwidth): log.debug('init-flow-installaton') try: olt = OltInstallFlowStateMachine( iface=self.interface, comm=self.comm, target=self.target, device=self.device_manager, onu_id=onu_id, channel_id=channel_id, port_id=port_id, onu_session_id=onu_session_id, alloc_id=alloc_id, svlan_id=svlan, cvlan_id=cvlan, uplink_bandwidth=uplink_bandwidth, downlink_bandwidth=downlink_bandwidth) olt.runbg() except Exception as e: log.exception('failed-to-launch-install-flow', e=e) def uninstall_flows_sequence(self, device, onu_id, port_id, alloc_id, onu_session_id, channel_id): log.debug('init-flow-deinstallaton') try: olt = OltRemoveFlowStateMachine(iface=self.interface, comm=self.comm, target=self.target, device=self.device_manager, onu_id=onu_id, channel_id=channel_id, port_id=port_id, onu_session_id=onu_session_id, alloc_id=alloc_id) olt.runbg() except Exception as e: log.exception('failed-to-launch-deinstallaton-flow', e=e) def _abandon(self, target): self.olt.stop() self.activation.stop() # Method exposed to Activation Watcher to get onu info from Activation def add_onu_info(self, port, onu_id, onu_session_id): existing_port = self.ports.get(port) if existing_port: existing_port['onu_id'] = onu_id existing_port['onu_session_id'] = onu_session_id else: self.ports[port] = { 'onu_id': onu_id, 'onu_session_id': onu_session_id } def send_proxied_message(self, proxy_address, msg): log.debug("send-proxied-message-olt-handler", proxy_address=proxy_address) if isinstance(msg, OmciFrame): omci_proxy = OMCIProxy(proxy_address=proxy_address, msg=msg, adapter_agent=self.adaptor_agent, target=self.device.mac_address, comm=self.comm, iface=self.interface) omci_proxy.runbg() else: api_proxy = APIProxy(proxy_address=proxy_address, msg=msg, adapter_agent=self.adaptor_agent, target=self.device.mac_address, comm=self.comm, iface=self.interface) api_proxy.runbg()
class PmcsOnu(object): name = 'pmcs_onu' supported_device_types = [ DeviceType( id=name, adapter=name, accepts_bulk_flow_update=True ) ] def __init__(self, adapter_agent, config): self.adapter_agent = adapter_agent self.config = config self.descriptor = Adapter( id=self.name, vendor='PMCS', version='0.1', config=AdapterConfig(log_level=LogLevel.INFO) ) def start(self): log.debug('starting') log.info('started') def stop(self): log.debug('stopping') log.info('stopped') def adapter_descriptor(self): return self.descriptor def device_types(self): return DeviceTypes(items=self.supported_device_types) def health(self): return HealthStatus(state=HealthStatus.HealthState.HEALTHY) def change_master_state(self, master): raise NotImplementedError() def adopt_device(self, device): return device def abandon_device(self, device): raise NotImplementedError() def deactivate_device(self, device): raise NotImplementedError() def update_flows_bulk(self, device, flows, groups): log.info('bulk-flow-update', device_id=device.id, flows=flows, groups=groups) def update_flows_incrementally(self, device, flow_changes, group_changes): raise NotImplementedError() def send_proxied_message(self, proxy_address, msg): log.info('send-proxied-message', proxy_address=proxy_address, msg=msg) def receive_proxied_message(self, proxy_address, msg): log.info('receive-proxied-message', proxy_address=proxy_address, device_id=proxy_address.device_id, msg=msg) def receive_packet_out(self, logical_device_id, egress_port_no, msg): log.info('packet-out', logical_device_id=logical_device_id, egress_port_no=egress_port_no, msg_len=len(msg))
class RubyAdapter(object): name = "microsemi_olt" supported_device_types = [ DeviceType(id=name, adapter=name, accepts_bulk_flow_update=True) ] def __init__(self, adaptor_agent, config): self.adaptor_agent = adaptor_agent self.config = config self.device_handlers = dict() self.descriptor = Adapter( id=self.name, vendor='Microsemi / Celestica', version='0.2', config=AdapterConfig(log_level=LogLevel.INFO)) def start(self): log.debug('starting') log.info('started') return self def stop(self): log.debug('stopping') for handler in self.device_handlers: handler.stop() log.info('stopped') return self def adapter_descriptor(self): return self.descriptor def device_types(self): return DeviceTypes(items=self.supported_device_types) def health(self): return HealthStatus(state=HealthStatus.HealthState.HEALTHY) def change_master_state(self, master): raise NotImplementedError() def adopt_device(self, device): log.debug('adopt-device', device=device) self.device_handlers[device.id] = RubyAdapterHandler( self.adaptor_agent, self.config, self.descriptor) reactor.callLater(0, self.device_handlers[device.id].activate, device) def reconcile_device(self, device): raise NotImplementedError() def abandon_device(self, device): self.stop() def disable_device(self, device): raise NotImplementedError() def reenable_device(self, device): raise NotImplementedError() def update_pm_config(self, device, pm_configs): raise NotImplementedError() def reboot_device(self, device): log.debug('reboot-device', device=device) device_handler = self.device_handlers[device.id] reactor.callLater(0, device_handler.reboot_device, device) def create_tcont(self, device, tcont_data, traffic_descriptor_data): raise NotImplementedError() def update_tcont(self, device, tcont_data, traffic_descriptor_data): raise NotImplementedError() def remove_tcont(self, device, tcont_data, traffic_descriptor_data): raise NotImplementedError() def create_gemport(self, device, data): raise NotImplementedError() def update_gemport(self, device, data): raise NotImplementedError() def remove_gemport(self, device, data): raise NotImplementedError() def create_multicast_gemport(self, device, data): raise NotImplementedError() def update_multicast_gemport(self, device, data): raise NotImplementedError() def remove_multicast_gemport(self, device, data): raise NotImplementedError() def create_multicast_distribution_set(self, device, data): raise NotImplementedError() def update_multicast_distribution_set(self, device, data): raise NotImplementedError() def remove_multicast_distribution_set(self, device, data): raise NotImplementedError() def download_image(self, device, request): raise NotImplementedError() def get_image_download_status(self, device, request): raise NotImplementedError() def cancel_image_download(self, device, request): raise NotImplementedError() def activate_image_update(self, device, request): raise NotImplementedError() def revert_image_update(self, device, request): raise NotImplementedError() def self_test_device(self, device): log.debug('self-test-device', device=device.id) raise NotImplementedError() def delete_device(self, device): raise NotImplementedError() def get_device_details(self, device): raise NotImplementedError() def update_flows_bulk(self, device, flows, groups): try: log.debug('olt-bulk-flow-update', device_id=device.id, flows=flows, groups=groups) handler = self.device_handlers[device.id] if handler: handler.update_flow_table(device, flows) else: log.debug("No handler found for device {}".format(device.id)) except Exception as e: log.exception('failed-olt-bulk-flow-update', e=e) def create_interface(self, device, data): raise NotImplementedError() def update_interface(self, device, data): raise NotImplementedError() def remove_interface(self, device, data): raise NotImplementedError() def receive_onu_detect_state(self, device_id, state): raise NotImplementedError() def send_proxied_message(self, proxy_address, msg): log.debug("send-proxied-message-olt", proxy_address=proxy_address) device = self.adaptor_agent.get_device(proxy_address.device_id) self.device_handlers[device.id].send_proxied_message( proxy_address, msg) def receive_proxied_message(self, proxy_address, msg): log.debug("receive-proxied-message-olt-handler", proxy_address=proxy_address) raise NotImplementedError() def update_flows_incrementally(self, device, flow_changes, group_changes): raise NotImplementedError() def receive_packet_out(self, logical_device_id, egress_port_no, msg): log.debug('packet-out', logical_device_id=logical_device_id, egress_port_no=egress_port_no, msg_len=len(msg)) def receive_inter_adapter_message(self, msg): raise NotImplementedError() def suppress_alarm(self, filter): raise NotImplementedError() def unsuppress_alarm(self, filter): raise NotImplementedError()
class BroadcomOnuAdapter(object): name = 'broadcom_onu' supported_device_types = [ DeviceType(id=name, adapter=name, accepts_bulk_flow_update=True) ] def __init__(self, adapter_agent, config): self.adapter_agent = adapter_agent self.config = config self.descriptor = Adapter( id=self.name, vendor='Voltha project', version='0.4', config=AdapterConfig(log_level=LogLevel.INFO)) self.devices_handlers = dict() # device_id -> BroadcomOnuHandler() def start(self): log.debug('starting') log.info('started') def stop(self): log.debug('stopping') log.info('stopped') def adapter_descriptor(self): return self.descriptor def device_types(self): return DeviceTypes(items=self.supported_device_types) def health(self): return HealthStatus(state=HealthStatus.HealthState.HEALTHY) def change_master_state(self, master): raise NotImplementedError() def adopt_device(self, device): log.info('adopt_device', device_id=device.id) self.devices_handlers[ device.proxy_address.channel_id] = BroadcomOnuHandler( self, device.id) reactor.callLater( 0, self.devices_handlers[device.proxy_address.channel_id].activate, device) return device def abandon_device(self, device): raise NotImplementedError() def disable_device(self, device): raise NotImplementedError() def reenable_device(self, device): raise NotImplementedError() def reboot_device(self, device): raise NotImplementedError() def delete_device(self, device): raise NotImplementedError() def get_device_details(self, device): raise NotImplementedError() def update_pm_config(self, device, pm_configs): raise NotImplementedError() def update_flows_bulk(self, device, flows, groups): log.info('bulk-flow-update', device_id=device.id, flows=flows, groups=groups) assert len(groups.items) == 0 handler = self.devices_handlers[device.proxy_address.channel_id] return handler.update_flow_table(device, flows.items) def update_flows_incrementally(self, device, flow_changes, group_changes): raise NotImplementedError() def send_proxied_message(self, proxy_address, msg): log.info('send-proxied-message', proxy_address=proxy_address, msg=msg) def receive_proxied_message(self, proxy_address, msg): log.info('receive-proxied-message', proxy_address=proxy_address, device_id=proxy_address.device_id, msg=hexify(msg)) handler = self.devices_handlers[proxy_address.channel_id] handler.receive_message(msg) def receive_packet_out(self, logical_device_id, egress_port_no, msg): log.info('packet-out', logical_device_id=logical_device_id, egress_port_no=egress_port_no, msg_len=len(msg))
class TibitOltAdapter(object): name = 'tibit_olt' supported_device_types = [ DeviceType( id='tibit_olt', adapter=name, accepts_bulk_flow_update=True ) ] def __init__(self, adapter_agent, config): self.adapter_agent = adapter_agent self.config = config self.descriptor = Adapter( id=self.name, vendor='Tibit Communications Inc.', version='0.1', config=AdapterConfig(log_level=LogLevel.INFO) ) self.interface = registry('main').get_args().interface self.io_port = None self.incoming_queues = {} # OLT mac_address -> DeferredQueue() self.device_ids = {} # OLT mac_address -> device_id self.vlan_to_device_ids = {} # c-vid -> (device_id, logical_device_id) def start(self): log.debug('starting', interface=self.interface) log.info('started', interface=self.interface) def stop(self): log.debug('stopping') if self.io_port is not None: registry('frameio').close_port(self.io_port) log.info('stopped') def adapter_descriptor(self): return self.descriptor def device_types(self): return DeviceTypes(items=self.supported_device_types) def health(self): return HealthStatus(state=HealthStatus.HealthState.HEALTHY) def change_master_state(self, master): raise NotImplementedError() def adopt_device(self, device): log.info('adopt-device', device=device) self._activate_io_port() reactor.callLater(0, self._launch_device_activation, device) def _activate_io_port(self): if self.io_port is None: self.io_port = registry('frameio').open_port( self.interface, self._rcv_io, is_tibit_frame) @inlineCallbacks def _launch_device_activation(self, device): try: log.debug('launch_dev_activation') # prepare receive queue self.incoming_queues[device.mac_address] = DeferredQueue(size=100) # add mac_address to device_ids table olt_mac = device.mac_address self.device_ids[olt_mac] = device.id # send out ping to OLT device ping_frame = self._make_ping_frame(mac_address=olt_mac) self.io_port.send(ping_frame) # wait till we receive a response ## TODO add timeout mechanism so we can signal if we cannot reach ##device while True: response = yield self.incoming_queues[olt_mac].get() # verify response and if not the expected response if 1: # TODO check if it is really what we expect, and wait if not break except Exception as e: log.exception('launch device failed', e=e) # if we got response, we can fill out the device info, mark the device # reachable jdev = json.loads(response.payload.payload.body.load) device.root = True device.vendor = 'Tibit Communications, Inc.' device.model = jdev.get('results', {}).get('device', 'DEVICE_UNKNOWN') device.hardware_version = jdev['results']['datecode'] device.firmware_version = jdev['results']['firmware'] device.software_version = jdev['results']['modelversion'] device.serial_number = jdev['results']['manufacturer'] device.connect_status = ConnectStatus.REACHABLE self.adapter_agent.update_device(device) # then shortly after we create some ports for the device log.info('create-port') nni_port = Port( port_no=2, label='NNI facing Ethernet port', type=Port.ETHERNET_NNI, admin_state=AdminState.ENABLED, oper_status=OperStatus.ACTIVE ) self.adapter_agent.add_port(device.id, nni_port) self.adapter_agent.add_port(device.id, Port( port_no=1, label='PON port', type=Port.PON_OLT, admin_state=AdminState.ENABLED, oper_status=OperStatus.ACTIVE )) log.info('create-logical-device') # then shortly after we create the logical device with one port # that will correspond to the NNI port ld = LogicalDevice( desc=ofp_desc( mfr_desc=device.vendor, hw_desc=jdev['results']['device'], sw_desc=jdev['results']['firmware'], serial_num=uuid4().hex, dp_desc='n/a' ), switch_features=ofp_switch_features( n_buffers=256, # TODO fake for now n_tables=2, # TODO ditto capabilities=( # TODO and ditto OFPC_FLOW_STATS | OFPC_TABLE_STATS | OFPC_PORT_STATS | OFPC_GROUP_STATS ) ), root_device_id=device.id ) ld_initialized = self.adapter_agent.create_logical_device(ld) cap = OFPPF_10GB_FD | OFPPF_FIBER self.adapter_agent.add_logical_port(ld_initialized.id, LogicalPort( id='nni', ofp_port=ofp_port( port_no=0, hw_addr=mac_str_to_tuple(device.mac_address), name='nni', config=0, state=OFPPS_LIVE, curr=cap, advertised=cap, peer=cap, curr_speed=OFPPF_10GB_FD, max_speed=OFPPF_10GB_FD ), device_id=device.id, device_port_no=nni_port.port_no, root_port=True )) # and finally update to active device = self.adapter_agent.get_device(device.id) device.parent_id = ld_initialized.id device.oper_status = OperStatus.ACTIVE self.adapter_agent.update_device(device) # Just transitioned to ACTIVE, wait a tenth of second # before checking for ONUs reactor.callLater(0.1, self._detect_onus, device) @inlineCallbacks def _detect_onus(self, device): # send out get 'links' to the OLT device olt_mac = device.mac_address links_frame = self._make_links_frame(mac_address=olt_mac) self.io_port.send(links_frame) while True: response = yield self.incoming_queues[olt_mac].get() # verify response and if not the expected response if 1: # TODO check if it is really what we expect, and wait if not break jdev = json.loads(response.payload.payload.body.load) onu_mac = '' for macid in jdev['results']: if macid['macid'] is None: log.info('MAC ID is NONE %s' % str(macid['macid'])) elif macid['macid'][:6].upper() == SUMITOMO_ELECTRIC_INDUSTRIES_OUI: onu_mac = macid['macid'] log.info('SUMITOMO mac address %s' % str(macid['macid'])) log.info('activate-olt-for-onu-%s' % onu_mac) # Convert from string to colon separated form onu_mac = ':'.join(s.encode('hex') for s in onu_mac.decode('hex')) vlan_id = self._olt_side_onu_activation(int(macid['macid'][-4:-2], 16)) self.adapter_agent.child_device_detected( parent_device_id=device.id, parent_port_no=1, child_device_type='dpoe_onu', mac_address = onu_mac, proxy_address=Device.ProxyAddress( device_id=device.id, channel_id=vlan_id ), vlan=vlan_id ) else: onu_mac = '000c' + macid.get('macid', 'e2000000')[4:] log.info('activate-olt-for-onu-%s' % onu_mac) # Convert from string to colon separated form onu_mac = ':'.join(s.encode('hex') for s in onu_mac.decode('hex')) vlan_id = self._olt_side_onu_activation(int(macid['macid'][-4:-2], 16)) self.adapter_agent.child_device_detected( parent_device_id=device.id, parent_port_no=1, child_device_type='tibit_onu', mac_address = onu_mac, proxy_address=Device.ProxyAddress( device_id=device.id, channel_id=vlan_id ), vlan=vlan_id ) # also record the vlan_id -> (device_id, logical_device_id, linkid) for # later use. The linkid is the macid returned. self.vlan_to_device_ids[vlan_id] = (device.id, device.parent_id, macid.get('macid', 0)) # Give the ONUs a chance to arrive before starting metric collection reactor.callLater(5.0, self.start_kpi_collection, device.id) def _olt_side_onu_activation(self, serial): """ This is where if this was a real OLT, the OLT-side activation for the new ONU should be performed. By the time we return, the OLT shall be able to provide tunneled (proxy) communication to the given ONU, using the returned information. """ vlan_id = serial + 200 return vlan_id def _rcv_io(self, port, frame): log.info('frame-received', frame=hexify(frame)) # make into frame to extract source mac response = Ether(frame) if response.haslayer(Dot1Q): # All OAM responses from the OLT should have a TIBIT_MGMT_VLAN. # Responses from the ONUs should have a TIBIT_MGMT_VLAN followed by a ONU CTAG # All packet-in frames will have the TIBIT_PACKET_IN_VLAN. if response.getlayer(Dot1Q).type == 0x8100: if response.getlayer(Dot1Q).vlan == TIBIT_PACKET_IN_VLAN: inner_tag_and_rest = response.payload.payload if isinstance(inner_tag_and_rest, Dot1Q): cvid = inner_tag_and_rest.vlan frame = Ether(src=response.src, dst=response.dst, type=inner_tag_and_rest.type) /\ inner_tag_and_rest.payload _, logical_device_id = self.vlan_to_device_ids.get(cvid) if logical_device_id is None: log.error('invalid-cvid', cvid=cvid) else: self.adapter_agent.send_packet_in( logical_device_id=logical_device_id, logical_port_no=cvid, # C-VID encodes port no packet=str(frame)) else: log.error('packet-in-single-tagged', frame=hexify(response)) else: ## Mgmt responses received from the ONU ## Since the type of the first layer is 0x8100, ## then the frame must have an inner tag layer olt_mac = response.src device_id = self.device_ids[olt_mac] channel_id = response[Dot1Q:2].vlan log.info('received_channel_id', channel_id=channel_id, device_id=device_id) proxy_address=Device.ProxyAddress( device_id=device_id, channel_id=channel_id ) # pop dot1q header(s) msg = response.payload.payload self.adapter_agent.receive_proxied_message(proxy_address, msg) else: ## Mgmt responses received from the OLT ## enqueue incoming parsed frame to right device log.info('received-dot1q-not-8100') self.incoming_queues[response.src].put(response) def _make_ping_frame(self, mac_address): # Create a json packet json_operation_str = '{\"operation\":\"version\"}' frame = Ether(dst=mac_address)/Dot1Q(vlan=TIBIT_MGMT_VLAN, prio=TIBIT_MGMT_PRIORITY)/TBJSON(data='json %s' % json_operation_str) return str(frame) def _make_links_frame(self, mac_address): # Create a json packet json_operation_str = '{\"operation\":\"links\"}' frame = Ether(dst=mac_address)/Dot1Q(vlan=TIBIT_MGMT_VLAN, prio=TIBIT_MGMT_PRIORITY)/TBJSON(data='json %s' % json_operation_str) return str(frame) def _make_stats_frame(self, mac_address, itype, link): # Create a json packet json_operation_str = ('{\"operation\":\"stats\",\"parameters\":{\"itype\":\"%s\",\"iinst\",\"0\",\"macid\":\"%s\"}}' % (itype, link)) frame = Ether(dst=mac_address)/Dot1Q(vlan=TIBIT_MGMT_VLAN, prio=TIBIT_MGMT_PRIORITY)/TBJSON(data='json %s' % json_operation_str) return str(frame) def abandon_device(self, device): raise NotImplementedError(0 ) def deactivate_device(self, device): raise NotImplementedError() def update_flows_bulk(self, device, flows, groups): log.info('########################################') log.info('bulk-flow-update', device_id=device.id, flows=flows, groups=groups) assert len(groups.items) == 0, "Cannot yet deal with groups" # extract ONU VID vid_from_device_id = {v[0]: k for k,v in self.vlan_to_device_ids.iteritems()} ONU_VID = vid_from_device_id[device.id] Clause = {v: k for k, v in ClauseSubtypeEnum.iteritems()} Operator = {v: k for k, v in RuleOperatorEnum.iteritems()} for flow in flows.items: try: in_port = get_in_port(flow) assert in_port is not None precedence = 255 - min(flow.priority / 256, 255) if in_port == 2: log.info('#### Downstream Rule ####') dn_req = NetworkToNetworkPortObject() dn_req /= PortIngressRuleHeader(precedence=precedence) for field in get_ofb_fields(flow): if field.type == ETH_TYPE: _type = field.eth_type log.info('#### field.type == ETH_TYPE ####') dn_req /= PortIngressRuleClauseMatchLength02( fieldcode=Clause['L2 Type/Len'], operator=Operator['=='], match=_type) elif field.type == IP_PROTO: _proto = field.ip_proto log.info('#### field.type == IP_PROTO ####') elif field.type == IN_PORT: _port = field.port log.info('#### field.type == IN_PORT ####', port=_port) elif field.type == VLAN_VID: _vlan_vid = field.vlan_vid & 0xfff log.info('#### field.type == VLAN_VID ####', vlan=_vlan_vid) dn_req /= PortIngressRuleClauseMatchLength02(fieldcode=Clause['C-VLAN Tag'], fieldinstance=0, operator=Operator['=='], match=_vlan_vid) if (_vlan_vid != 140): dn_req /= PortIngressRuleClauseMatchLength02(fieldcode=Clause['C-VLAN Tag'], fieldinstance=1, operator=Operator['=='], match=ONU_VID) elif field.type == VLAN_PCP: _vlan_pcp = field.vlan_pcp log.info('#### field.type == VLAN_PCP ####', pcp=_vlan_pcp) elif field.type == UDP_DST: _udp_dst = field.udp_dst log.info('#### field.type == UDP_DST ####') elif field.type == UDP_SRC: _udp_src = field.udp_src log.info('#### field.type == UDP_SRC ####') elif field.type == IPV4_DST: _ipv4_dst = field.ipv4_dst log.info('#### field.type == IPV4_DST ####') elif field.type == METADATA: log.info('#### field.type == METADATA ####') pass else: raise NotImplementedError('field.type={}'.format( field.type)) for action in get_actions(flow): if action.type == OUTPUT: log.info('#### action.type == OUTPUT ####') dn_req /= PortIngressRuleResultForward() serial = ONU_VID - 200 link = (0xe222 << 16) | (serial << 8) dn_req /= PortIngressRuleResultOLTQueue(unicastvssn="TBIT", unicastlink=link) elif action.type == POP_VLAN: log.info('#### action.type == POP_VLAN ####') dn_req /= PortIngressRuleResultDelete(fieldcode=Clause['S-VLAN Tag']) elif action.type == PUSH_VLAN: log.info('#### action.type == PUSH_VLAN ####') if action.push.ethertype != 0x8100: log.error('unhandled-tpid', ethertype=action.push.ethertype) dn_req /= PortIngressRuleResultInsert(fieldcode=Clause['C-VLAN Tag']) elif action.type == SET_FIELD: log.info('#### action.type == SET_FIELD ####') assert (action.set_field.field.oxm_class == ofp.OFPXMC_OPENFLOW_BASIC) field = action.set_field.field.ofb_field if field.type == VLAN_VID: dn_req /= PortIngressRuleResultSet( fieldcode=Clause['C-VLAN Tag'], value=field.vlan_vid & 0xfff) else: log.error('unsupported-action-set-field-type', field_type=field.type) else: log.error('UNSUPPORTED-ACTION-TYPE', action_type=action.type) dn_req /= PortIngressRuleTerminator() dn_req /= AddPortIngressRule() msg = ( Ether(dst=device.mac_address) / Dot1Q(vlan=TIBIT_MGMT_VLAN, prio=TIBIT_MGMT_PRIORITY) / EOAMPayload( body=CablelabsOUI() / DPoEOpcode_SetRequest() / dn_req) ) self.io_port.send(str(msg)) elif in_port == 1: # Upstream rule log.info('#### Upstream Rule ####') field_match_vlan_upstream_with_link = False up_req_link = PortIngressRuleHeader(precedence=precedence) up_req_pon = PonPortObject() up_req_pon /= PortIngressRuleHeader(precedence=precedence) for field in get_ofb_fields(flow): if field.type == ETH_TYPE: _type = field.eth_type log.info('#### field.type == ETH_TYPE ####', in_port=in_port, match=_type) up_req_pon /= PortIngressRuleClauseMatchLength02( fieldcode=Clause['L2 Type/Len'], operator=Operator['=='], match=_type) up_req_link /= PortIngressRuleClauseMatchLength02( fieldcode=Clause['L2 Type/Len'], operator=Operator['=='], match=_type) elif field.type == IP_PROTO: _proto = field.ip_proto log.info('#### field.type == IP_PROTO ####', in_port=in_port, ip_proto=_proto) up_req_pon /= PortIngressRuleClauseMatchLength01( fieldcode=Clause['IPv4/IPv6 Protocol Type'], operator=Operator['=='], match=_proto) up_req_link /= PortIngressRuleClauseMatchLength01( fieldcode=Clause['IPv4/IPv6 Protocol Type'], operator=Operator['=='], match=_proto) elif field.type == IN_PORT: _port = field.port log.info('#### field.type == IN_PORT ####') elif field.type == VLAN_VID: _vlan_vid = field.vlan_vid & 0xfff log.info('#### field.type == VLAN_VID ####') up_req_pon /= PortIngressRuleClauseMatchLength02( fieldcode=Clause['C-VLAN Tag'], fieldinstance=0, operator=Operator['=='], match=_vlan_vid) serial = _vlan_vid - 200 link = (0xe222 << 16) | (serial << 8) up_req_link /= OLTUnicastLogicalLink(unicastvssn='TBIT', unicastlink=link) up_req_link /= PortIngressRuleClauseMatchLength02( fieldcode=Clause['C-VLAN Tag'], fieldinstance=0, operator=Operator['=='], match=_vlan_vid) field_match_vlan_upstream_with_link = True elif field.type == VLAN_PCP: _vlan_pcp = field.vlan_pcp log.info('#### field.type == VLAN_PCP ####') elif field.type == UDP_DST: _udp_dst = field.udp_dst log.info('#### field.type == UDP_DST ####') up_req_pon /= (PortIngressRuleClauseMatchLength02(fieldcode=Clause['TCP/UDP source port'], operator=Operator['=='], match=0x0044)/ PortIngressRuleClauseMatchLength02(fieldcode=Clause['TCP/UDP destination port'], operator=Operator['=='], match=0x0043)) elif field.type == UDP_SRC: _udp_src = field.udp_src log.info('#### field.type == UDP_SRC ####') else: raise NotImplementedError('field.type={}'.format( field.type)) for action in get_actions(flow): if action.type == OUTPUT: log.info('#### action.type == OUTPUT ####') up_req_pon /= PortIngressRuleResultForward() up_req_link /= PortIngressRuleResultForward() elif action.type == POP_VLAN: log.info('#### action.type == POP_VLAN ####') elif action.type == PUSH_VLAN: log.info('#### action.type == PUSH_VLAN ####') if action.push.ethertype != 0x8100: log.error('unhandled-ether-type', ethertype=action.push.ethertype) if field_match_vlan_upstream_with_link == True: up_req_link /= PortIngressRuleResultInsert(fieldcode=Clause['C-VLAN Tag'], fieldinstance=1) else: up_req_pon /= PortIngressRuleResultInsert(fieldcode=Clause['C-VLAN Tag'], fieldinstance=0) elif action.type == SET_FIELD: log.info('#### action.type == SET_FIELD ####') assert (action.set_field.field.oxm_class == ofp.OFPXMC_OPENFLOW_BASIC) field = action.set_field.field.ofb_field if field.type == VLAN_VID: if field_match_vlan_upstream_with_link == True: up_req_link /=(PortIngressRuleResultCopy(fieldcode=Clause['C-VLAN Tag'])/ PortIngressRuleResultReplace(fieldcode=Clause['C-VLAN Tag'])) up_req_pon /= PortIngressRuleResultSet( fieldcode=Clause['C-VLAN Tag'], value=field.vlan_vid & 0xfff) up_req_link /= PortIngressRuleResultSet( fieldcode=Clause['C-VLAN Tag'], value=field.vlan_vid & 0xfff) else: log.error('unsupported-action-set-field-type', field_type=field.type) else: log.error('UNSUPPORTED-ACTION-TYPE', action_type=action.type) if (field_match_vlan_upstream_with_link == True): up_req = up_req_link else: up_req = up_req_pon up_req /= PortIngressRuleTerminator() up_req /= AddPortIngressRule() msg = ( Ether(dst=device.mac_address) / Dot1Q(vlan=TIBIT_MGMT_VLAN, prio=TIBIT_MGMT_PRIORITY) / EOAMPayload( body=CablelabsOUI() / DPoEOpcode_SetRequest() / up_req) ) self.io_port.send(str(msg)) else: raise Exception('Port should be 1 or 2 by our convention') except Exception, e: log.exception('failed-to-install-flow', e=e, flow=flow)
class OpenoltAdapter(object): name = 'openolt' supported_device_types = [ DeviceType(id=name, adapter=name, accepts_bulk_flow_update=True) ] def __init__(self, adapter_agent, config): self.adapter_agent = adapter_agent self.config = config self.descriptor = Adapter( id=self.name, vendor='OLT white box vendor', version='0.1', config=AdapterConfig(log_level=LogLevel.INFO)) log.debug('openolt.__init__', adapter_agent=adapter_agent) self.devices = dict() # device_id -> OpenoltDevice() self.interface = registry('main').get_args().interface self.logical_device_id_to_root_device_id = dict() def start(self): log.info('started', interface=self.interface) def stop(self): log.info('stopped', interface=self.interface) def adapter_descriptor(self): log.debug('get descriptor', interface=self.interface) return self.descriptor def device_types(self): log.debug('get device_types', interface=self.interface, items=self.supported_device_types) return DeviceTypes(items=self.supported_device_types) def health(self): log.debug('get health', interface=self.interface) raise NotImplementedError() def change_master_state(self, master): log.debug('change_master_state', interface=self.interface, master=master) raise NotImplementedError() def adopt_device(self, device): log.info('adopt-device', device=device) kwargs = {'adapter_agent': self.adapter_agent, 'device': device} try: self.devices[device.id] = OpenoltDevice(**kwargs) except Exception as e: log.error('Failed to adopt OpenOLT device', error=e) del self.devices[device.id] raise def reconcile_device(self, device): log.info('reconcile-device', device=device) raise NotImplementedError() def abandon_device(self, device): log.info('abandon-device', device=device) raise NotImplementedError() def disable_device(self, device): log.info('disable-device', device=device) raise NotImplementedError() def reenable_device(self, device): log.info('reenable-device', device=device) raise NotImplementedError() def reboot_device(self, device): log.info('reboot_device', device=device) raise NotImplementedError() def download_image(self, device, request): log.info('image_download', device=device, request=request) raise NotImplementedError() def get_image_download_status(self, device, request): log.info('get_image_download', device=device, request=request) raise NotImplementedError() def cancel_image_download(self, device, request): log.info('cancel_image_download', device=device) raise NotImplementedError() def activate_image_update(self, device, request): log.info('activate_image_update', device=device, request=request) raise NotImplementedError() def revert_image_update(self, device, request): log.info('revert_image_update', device=device, request=request) raise NotImplementedError() def self_test_device(self, device): from voltha.protos.voltha_pb2 import SelfTestResponse raise NotImplementedError() def delete_device(self, device): log.info('delete-device', device=device) raise NotImplementedError() def get_device_details(self, device): log.debug('get_device_details', device=device) raise NotImplementedError() def update_flows_bulk(self, device, flows, groups): log.info('bulk-flow-update', device_id=device.id, flows=flows, groups=groups) assert len(groups.items) == 0, "Cannot yet deal with groups" handler = self.devices[device.id] return handler.update_flow_table(flows.items) def update_flows_incrementally(self, device, flow_changes, group_changes): log.debug('update_flows_incrementally', device=device, flow_changes=flow_changes, group_changes=group_changes) raise NotImplementedError() def update_pm_config(self, device, pm_configs): log.debug('update_pm_config', device=device, pm_configs=pm_configs) raise NotImplementedError() def send_proxied_message(self, proxy_address, msg): log.debug('send-proxied-message', proxy_address=proxy_address, msg=msg) handler = self.devices[proxy_address.device_id] handler.send_proxied_message(proxy_address, msg) def receive_proxied_message(self, proxy_address, msg): log.debug('receive_proxied_message', proxy_address=proxy_address, msg=msg) raise NotImplementedError() def receive_packet_out(self, logical_device_id, egress_port_no, msg): log.debug('packet-out', logical_device_id=logical_device_id, egress_port_no=egress_port_no, msg_len=len(msg)) raise NotImplementedError() def receive_inter_adapter_message(self, msg): log.info('rx_inter_adapter_msg') raise NotImplementedError() def suppress_alarm(self, filter): log.info('suppress_alarm', filter=filter) raise NotImplementedError() def unsuppress_alarm(self, filter): log.info('unsuppress_alarm', filter=filter) raise NotImplementedError() # PON Mgnt APIs # def create_interface(self, device, data): log.debug('create-interface', data=data) raise NotImplementedError() def update_interface(self, device, data): log.debug('update-interface', data=data) raise NotImplementedError() def remove_interface(self, device, data): log.debug('remove-interface', data=data) raise NotImplementedError() def receive_onu_detect_state(self, proxy_address, state): log.debug('receive-onu-detect-state', data=data) raise NotImplementedError() def create_tcont(self, device, tcont_data, traffic_descriptor_data): log.info('create-tcont', tcont_data=tcont_data, traffic_descriptor_data=traffic_descriptor_data) raise NotImplementedError() def update_tcont(self, device, tcont_data, traffic_descriptor_data): log.info('update-tcont', tcont_data=tcont_data, traffic_descriptor_data=traffic_descriptor_data) raise NotImplementedError() def remove_tcont(self, device, tcont_data, traffic_descriptor_data): log.info('remove-tcont', tcont_data=tcont_data, traffic_descriptor_data=traffic_descriptor_data) raise NotImplementedError() def create_gemport(self, device, data): log.info('create-gemport', data=data) raise NotImplementedError() def update_gemport(self, device, data): log.info('update-gemport', data=data) raise NotImplementedError() def remove_gemport(self, device, data): log.info('remove-gemport', data=data) raise NotImplementedError() def create_multicast_gemport(self, device, data): log.info('create-mcast-gemport', data=data) raise NotImplementedError() def update_multicast_gemport(self, device, data): log.info('update-mcast-gemport', data=data) raise NotImplementedError() def remove_multicast_gemport(self, device, data): log.info('remove-mcast-gemport', data=data) raise NotImplementedError() def create_multicast_distribution_set(self, device, data): log.info('create-mcast-distribution-set', data=data) raise NotImplementedError() def update_multicast_distribution_set(self, device, data): log.info('update-mcast-distribution-set', data=data) raise NotImplementedError() def remove_multicast_distribution_set(self, device, data): log.info('remove-mcast-distribution-set', data=data) raise NotImplementedError()
class PonSimOltAdapter(object): name = 'ponsim_olt' supported_device_types = [ DeviceType( id=name, adapter=name, accepts_bulk_flow_update=True ) ] def __init__(self, adapter_agent, config): self.adapter_agent = adapter_agent self.config = config self.descriptor = Adapter( id=self.name, vendor='Voltha project', version='0.4', config=AdapterConfig(log_level=LogLevel.INFO) ) self.devices_handlers = dict() # device_id -> PonSimOltHandler() self.logical_device_id_to_root_device_id = dict() def start(self): log.debug('starting') log.info('started') def stop(self): """ This method is called when this device instance is no longer required, which means there is a request to remove this device. :return: """ log.debug('stopping') log.info('stopped') def adapter_descriptor(self): return self.descriptor def device_types(self): return DeviceTypes(items=self.supported_device_types) def health(self): return HealthStatus(state=HealthStatus.HealthState.HEALTHY) def change_master_state(self, master): raise NotImplementedError() def update_pm_config(self, device, pm_config): log.info("adapter-update-pm-config", device=device, pm_config=pm_config) handler = self.devices_handlers[device.id] handler.update_pm_config(device, pm_config) def adopt_device(self, device): self.devices_handlers[device.id] = PonSimOltHandler(self, device.id) reactor.callLater(0, self.devices_handlers[device.id].activate, device) return device def abandon_device(self, device): raise NotImplementedError() def disable_device(self, device): log.info('disable-device', device_id=device.id) reactor.callLater(0, self.devices_handlers[device.id].disable) return device def reenable_device(self, device): log.info('reenable-device', device_id=device.id) reactor.callLater(0, self.devices_handlers[device.id].reenable) return device def reboot_device(self, device): log.info('reboot-device', device_id=device.id) reactor.callLater(0, self.devices_handlers[device.id].reboot) return device def delete_device(self, device): log.info('delete-device', device_id=device.id) reactor.callLater(0, self.devices_handlers[device.id].delete) return device def get_device_details(self, device): raise NotImplementedError() def update_flows_bulk(self, device, flows, groups): log.info('bulk-flow-update', device_id=device.id, flows=flows, groups=groups) assert len(groups.items) == 0 handler = self.devices_handlers[device.id] return handler.update_flow_table(flows.items) def update_flows_incrementally(self, device, flow_changes, group_changes): raise NotImplementedError() def send_proxied_message(self, proxy_address, msg): log.info('send-proxied-message', proxy_address=proxy_address, msg=msg) handler = self.devices_handlers[proxy_address.device_id] handler.send_proxied_message(proxy_address, msg) def receive_proxied_message(self, proxy_address, msg): raise NotImplementedError() def receive_packet_out(self, logical_device_id, egress_port_no, msg): def ldi_to_di(ldi): di = self.logical_device_id_to_root_device_id.get(ldi) if di is None: logical_device = self.adapter_agent.get_logical_device(ldi) di = logical_device.root_device_id self.logical_device_id_to_root_device_id[ldi] = di return di device_id = ldi_to_di(logical_device_id) handler = self.devices_handlers[device_id] handler.packet_out(egress_port_no, msg) def receive_inter_adapter_message(self, msg): raise NotImplementedError() def suppress_alarm(self, filter): raise NotImplementedError() def unsuppress_alarm(self, filter): raise NotImplementedError()