VOL-705, VOL-706, VOL-711, VOL-713 OpenOMCI implementation of the Broadcom adapter.
Change-Id: If3f7e675979ba91b7ecb6deb61e06348cf150244
diff --git a/voltha/adapters/brcm_openomci_onu/__init__.py b/voltha/adapters/brcm_openomci_onu/__init__.py
new file mode 100644
index 0000000..b0fb0b2
--- /dev/null
+++ b/voltha/adapters/brcm_openomci_onu/__init__.py
@@ -0,0 +1,13 @@
+# Copyright 2017-present Open Networking Foundation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
diff --git a/voltha/adapters/brcm_openomci_onu/brcm_openomci_onu.py b/voltha/adapters/brcm_openomci_onu/brcm_openomci_onu.py
new file mode 100644
index 0000000..9424bba
--- /dev/null
+++ b/voltha/adapters/brcm_openomci_onu/brcm_openomci_onu.py
@@ -0,0 +1,321 @@
+#
+# Copyright 2017 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+"""
+Broadcom OpenOMCI OLT/ONU adapter.
+"""
+
+from twisted.internet import reactor, task
+from zope.interface import implementer
+
+from voltha.adapters.brcm_openomci_onu.brcm_openomci_onu_handler import BrcmOpenomciOnuHandler
+from voltha.adapters.interface import IAdapterInterface
+from voltha.protos import third_party
+from voltha.protos.adapter_pb2 import Adapter
+from voltha.protos.adapter_pb2 import AdapterConfig
+from voltha.protos.common_pb2 import LogLevel
+from voltha.protos.device_pb2 import DeviceType, DeviceTypes, Port, Image
+from voltha.protos.health_pb2 import HealthStatus
+
+from common.frameio.frameio import hexify
+from voltha.extensions.omci.openomci_agent import OpenOMCIAgent, OpenOmciAgentDefaults
+from voltha.extensions.omci.omci_me import *
+from omci.brcm_capabilities_task import BrcmCapabilitiesTask
+from omci.brcm_get_mds_task import BrcmGetMdsTask
+from omci.brcm_mib_sync import BrcmMibSynchronizer
+from copy import deepcopy
+
+
+_ = third_party
+log = structlog.get_logger()
+
+
+@implementer(IAdapterInterface)
+class BrcmOpenomciOnuAdapter(object):
+
+ name = 'brcm_openomci_onu'
+
+ supported_device_types = [
+ DeviceType(
+ id=name,
+ vendor_ids=['OPEN', 'ALCL', 'ALPH'],
+ 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['mib-synchronizer']['tasks']['get-mds'] = BrcmGetMdsTask
+ self.broadcom_omci['mib-synchronizer']['tasks']['mib-audit'] = BrcmGetMdsTask
+ self.broadcom_omci['omci-capabilities']['tasks']['get-capabilities'] = BrcmCapabilitiesTask
+
+ self._omci_agent = OpenOMCIAgent(self.adapter_agent.core,
+ support_classes=self.broadcom_omci)
+
+
+ # register for adapter messages
+ self.adapter_agent.register_for_inter_adapter_messages()
+
+ @property
+ def omci_agent(self):
+ 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', 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()
+
+ 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.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))
+ # 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.info('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.info('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.info('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.info('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.info('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.info('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.info('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.info('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.info('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()
+
+
diff --git a/voltha/adapters/brcm_openomci_onu/brcm_openomci_onu_handler.py b/voltha/adapters/brcm_openomci_onu/brcm_openomci_onu_handler.py
new file mode 100644
index 0000000..f825e5c
--- /dev/null
+++ b/voltha/adapters/brcm_openomci_onu/brcm_openomci_onu_handler.py
@@ -0,0 +1,940 @@
+#
+# Copyright 2017 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+"""
+Broadcom OpenOMCI OLT/ONU adapter handler.
+"""
+
+import structlog
+from twisted.internet import reactor, task
+from twisted.internet.defer import DeferredQueue, inlineCallbacks, returnValue, TimeoutError
+
+from common.frameio.frameio import hexify
+from common.utils.indexpool import IndexPool
+from voltha.core.logical_device_agent import mac_str_to_tuple
+import voltha.core.flow_decomposer as fd
+from voltha.registry import registry
+from voltha.protos import third_party
+from voltha.protos.common_pb2 import OperStatus, ConnectStatus, \
+ AdminState
+from voltha.protos.device_pb2 import Port, Image
+from voltha.protos.logical_device_pb2 import LogicalPort
+from voltha.protos.openflow_13_pb2 import OFPPS_LIVE, OFPPF_FIBER, OFPPF_1GB_FD, OFPPS_LINK_DOWN
+from voltha.protos.openflow_13_pb2 import OFPXMC_OPENFLOW_BASIC, ofp_port
+from voltha.protos.bbf_fiber_base_pb2 import VEnetConfig, VOntaniConfig
+from voltha.protos.bbf_fiber_tcont_body_pb2 import TcontsConfigData
+from voltha.protos.bbf_fiber_gemport_body_pb2 import GemportsConfigData
+from voltha.extensions.omci.onu_configuration import OMCCVersion
+from voltha.extensions.omci.onu_device_entry import OnuDeviceEvents, \
+ OnuDeviceEntry, IN_SYNC_KEY
+from voltha.extensions.omci.omci_me import *
+from voltha.adapters.brcm_openomci_onu.omci.brcm_mib_download_task import BrcmMibDownloadTask
+from voltha.adapters.brcm_openomci_onu.onu_gem_port import *
+from voltha.adapters.brcm_openomci_onu.onu_tcont import *
+from voltha.adapters.brcm_openomci_onu.pon_port import *
+from voltha.adapters.brcm_openomci_onu.uni_port import *
+from voltha.adapters.brcm_openomci_onu.onu_traffic_descriptor import *
+
+import voltha.adapters.openolt.openolt_platform as platform
+
+OP = EntityOperations
+RC = ReasonCodes
+
+
+_ = third_party
+log = structlog.get_logger()
+
+
+BRDCM_DEFAULT_VLAN = 4091
+ADMIN_STATE_LOCK = 1
+ADMIN_STATE_UNLOCK = 0
+RESERVED_VLAN_ID = 4095
+_STARTUP_RETRY_WAIT = 5
+_MAXIMUM_PORT = 128 # UNI ports
+
+
+
+_ = third_party
+
+
+
+class BrcmOpenomciOnuHandler(object):
+
+ def __init__(self, adapter, device_id):
+ self.log = structlog.get_logger(device_id=device_id)
+ self.log.debug('function-entry')
+ self.adapter = adapter
+ self.adapter_agent = adapter.adapter_agent
+ self.device_id = device_id
+ self.incoming_messages = DeferredQueue()
+ self.event_messages = DeferredQueue()
+ self.proxy_address = None
+ self.tx_id = 0
+ self._enabled = False
+ self._omcc_version = OMCCVersion.Unknown
+ self._total_tcont_count = 0 # From ANI-G ME
+ self._qos_flexibility = 0 # From ONT2_G ME
+
+ self._onu_indication = None
+ self._unis = dict() # Port # -> UniPort
+ self._port_number_pool = IndexPool(_MAXIMUM_PORT, 0)
+
+ self._pon = None
+ #TODO: probably shouldnt be hardcoded, determine from olt maybe?
+ self._pon_port_number = 100
+ self.logical_device_id = None
+
+ # Set up OpenOMCI environment
+ self._onu_omci_device = None
+ self._dev_info_loaded = False
+ self._deferred = None
+
+ self._in_sync_subscription = None
+ self._connectivity_subscription = None
+ self._capabilities_subscription = None
+
+ @property
+ def enabled(self):
+ self.log.debug('function-entry')
+ return self._enabled
+
+ @enabled.setter
+ def enabled(self, value):
+ self.log.debug('function-entry')
+ if self._enabled != value:
+ self._enabled = value
+
+ @property
+ def omci_agent(self):
+ self.log.debug('function-entry')
+ return self.adapter.omci_agent
+
+ @property
+ def omci_cc(self):
+ self.log.debug('function-entry')
+ return self._onu_omci_device.omci_cc if self._onu_omci_device is not None else None
+
+ @property
+ def uni_ports(self):
+ self.log.debug('function-entry')
+ return self._unis.values()
+
+ def uni_port(self, port_no_or_name):
+ self.log.debug('function-entry')
+ if isinstance(port_no_or_name, (str, unicode)):
+ return next((uni for uni in self.uni_ports
+ if uni.name == port_no_or_name), None)
+
+ assert isinstance(port_no_or_name, int), 'Invalid parameter type'
+ return self._unis.get(port_no_or_name)
+
+ @property
+ def pon_port(self):
+ self.log.debug('function-entry')
+ return self._pon
+
+ @property
+ def _next_port_number(self):
+ self.log.debug('function-entry')
+ return self._port_number_pool.get_next()
+
+ def _release_port_number(self, number):
+ self.log.debug('function-entry', number=number)
+ self._port_number_pool.release(number)
+
+ def receive_message(self, msg):
+ self.log.debug('function-entry', msg=hexify(msg))
+ if self.omci_cc is not None:
+ self.omci_cc.receive_message(msg)
+
+ def activate(self, device):
+ self.log.debug('function-entry', device=device)
+
+ # first we verify that we got parent reference and proxy info
+ assert device.parent_id
+ assert device.proxy_address.device_id
+
+ # register for proxied messages right away
+ self.proxy_address = device.proxy_address
+ self.adapter_agent.register_for_proxied_messages(device.proxy_address)
+
+
+ # populate device info
+ device.root = True
+ device.vendor = 'Broadcom'
+ device.model = 'n/a'
+ device.hardware_version = 'to be filled'
+ device.firmware_version = 'to be filled'
+ device.images.image.extend([
+ Image(version="to be filled")
+ ])
+ device.connect_status = ConnectStatus.REACHABLE
+ self.adapter_agent.update_device(device)
+
+ self._pon = PonPort.create(self, self._pon_port_number)
+ self.adapter_agent.add_port(device.id, self._pon.get_port())
+
+ self.log.debug('added-pon-port-to-agent', pon=self._pon)
+
+ parent_device = self.adapter_agent.get_device(device.parent_id)
+ self.logical_device_id = parent_device.parent_id
+
+ device = self.adapter_agent.get_device(device.id)
+ device.oper_status = OperStatus.DISCOVERED
+ self.adapter_agent.update_device(device)
+
+ self.log.debug('set-device-discovered')
+
+ # Create and start the OpenOMCI ONU Device Entry for this ONU
+ self._onu_omci_device = self.omci_agent.add_device(self.device_id,
+ self.adapter_agent,
+ support_classes=self.adapter.broadcom_omci)
+ # Subscriber to events of interest in OpenOMCI
+ self._subscribe_to_events()
+
+ # Port startup
+ if self._pon is not None:
+ self._pon.enabled = True
+
+ self.enabled = True
+
+ self.log.debug('starting-openomci-engine')
+ reactor.callLater(5, self._onu_omci_device.start)
+
+ def reconcile(self, device):
+ self.log.debug('function-entry', device=device)
+
+ # first we verify that we got parent reference and proxy info
+ assert device.parent_id
+ assert device.proxy_address.device_id
+
+ # register for proxied messages right away
+ self.proxy_address = device.proxy_address
+ self.adapter_agent.register_for_proxied_messages(device.proxy_address)
+
+ # TODO: Query ONU current status after reconcile and update.
+ # To be addressed in future commits.
+
+ self.log.info('reconciling-broadcom-onu-device-ends')
+
+ # TODO: move to UniPort
+ def update_logical_port(self, logical_device_id, port_id, state):
+ try:
+ self.log.info('updating-logical-port', logical_port_id=port_id,
+ logical_device_id=logical_device_id, state=state)
+ logical_port = self.adapter_agent.get_logical_port(logical_device_id,
+ port_id)
+ logical_port.ofp_port.state = state
+ self.adapter_agent.update_logical_port(logical_device_id,
+ logical_port)
+ except Exception as e:
+ self.log.exception("exception-updating-port",e=e)
+
+ def delete(self, device):
+ self.log.debug('function-entry', device=device)
+ self.log.info('delete-onu')
+ # The device is already deleted in delete_v_ont_ani(). No more
+ # handling needed here
+
+ @inlineCallbacks
+ def update_flow_table(self, device, flows):
+ self.log.debug('function-entry', device=device, flows=flows)
+ #
+ # We need to proxy through the OLT to get to the ONU
+ # Configuration from here should be using OMCI
+ #
+ #self.log.info('bulk-flow-update', device_id=device.id, flows=flows)
+
+ def is_downstream(port):
+ return port == self._pon_port_number
+
+ def is_upstream(port):
+ return not is_downstream(port)
+
+ for flow in flows:
+ _type = None
+ _port = None
+ _vlan_vid = None
+ _udp_dst = None
+ _udp_src = None
+ _ipv4_dst = None
+ _ipv4_src = None
+ _metadata = None
+ _output = None
+ _push_tpid = None
+ _field = None
+ _set_vlan_vid = None
+ self.log.info('bulk-flow-update', device_id=device.id, flow=flow)
+ try:
+ _in_port = fd.get_in_port(flow)
+ assert _in_port is not None
+
+ if is_downstream(_in_port):
+ self.log.info('downstream-flow')
+ elif is_upstream(_in_port):
+ self.log.info('upstream-flow')
+ else:
+ raise Exception('port should be 1 or 2 by our convention')
+
+ _out_port = fd.get_out_port(flow) # may be None
+ self.log.info('out-port', out_port=_out_port)
+
+ for field in fd.get_ofb_fields(flow):
+ if field.type == fd.ETH_TYPE:
+ _type = field.eth_type
+ self.log.info('field-type-eth-type',
+ eth_type=_type)
+
+ elif field.type == fd.IP_PROTO:
+ _proto = field.ip_proto
+ self.log.info('field-type-ip-proto',
+ ip_proto=_proto)
+
+ elif field.type == fd.IN_PORT:
+ _port = field.port
+ self.log.info('field-type-in-port',
+ in_port=_port)
+
+ elif field.type == fd.VLAN_VID:
+ _vlan_vid = field.vlan_vid & 0xfff
+ self.log.info('field-type-vlan-vid',
+ vlan=_vlan_vid)
+
+ elif field.type == fd.VLAN_PCP:
+ _vlan_pcp = field.vlan_pcp
+ self.log.info('field-type-vlan-pcp',
+ pcp=_vlan_pcp)
+
+ elif field.type == fd.UDP_DST:
+ _udp_dst = field.udp_dst
+ self.log.info('field-type-udp-dst',
+ udp_dst=_udp_dst)
+
+ elif field.type == fd.UDP_SRC:
+ _udp_src = field.udp_src
+ self.log.info('field-type-udp-src',
+ udp_src=_udp_src)
+
+ elif field.type == fd.IPV4_DST:
+ _ipv4_dst = field.ipv4_dst
+ self.log.info('field-type-ipv4-dst',
+ ipv4_dst=_ipv4_dst)
+
+ elif field.type == fd.IPV4_SRC:
+ _ipv4_src = field.ipv4_src
+ self.log.info('field-type-ipv4-src',
+ ipv4_dst=_ipv4_src)
+
+ elif field.type == fd.METADATA:
+ _metadata = field.table_metadata
+ self.log.info('field-type-metadata',
+ metadata=_metadata)
+
+ else:
+ raise NotImplementedError('field.type={}'.format(
+ field.type))
+
+ for action in fd.get_actions(flow):
+
+ if action.type == fd.OUTPUT:
+ _output = action.output.port
+ self.log.info('action-type-output',
+ output=_output, in_port=_in_port)
+
+ elif action.type == fd.POP_VLAN:
+ self.log.info('action-type-pop-vlan',
+ in_port=_in_port)
+
+ elif action.type == fd.PUSH_VLAN:
+ _push_tpid = action.push.ethertype
+ self.log.info('action-type-push-vlan',
+ push_tpid=_push_tpid, in_port=_in_port)
+ if action.push.ethertype != 0x8100:
+ self.log.error('unhandled-tpid',
+ ethertype=action.push.ethertype)
+
+ elif action.type == fd.SET_FIELD:
+ _field = action.set_field.field.ofb_field
+ assert (action.set_field.field.oxm_class ==
+ OFPXMC_OPENFLOW_BASIC)
+ self.log.info('action-type-set-field',
+ field=_field, in_port=_in_port)
+ if _field.type == fd.VLAN_VID:
+ _set_vlan_vid = _field.vlan_vid & 0xfff
+ self.log.info('set-field-type-valn-vid', _set_vlan_vid)
+ else:
+ self.log.error('unsupported-action-set-field-type',
+ field_type=_field.type)
+ else:
+ self.log.error('unsupported-action-type',
+ action_type=action.type, in_port=_in_port)
+
+ #
+ # All flows created from ONU adapter should be OMCI based
+ #
+ if _vlan_vid == 0 and _set_vlan_vid != None and _set_vlan_vid != 0:
+
+ ## TODO: find a better place for all of this
+ ## TODO: make this a member of the onu gem port or the uni port
+ _mac_bridge_service_profile_entity_id = 0x201
+ _mac_bridge_port_ani_entity_id = 0x2102 # TODO: can we just use the entity id from the anis list?
+
+ # Delete bridge ani side vlan filter
+ msg = VlanTaggingFilterDataFrame(_mac_bridge_port_ani_entity_id)
+ frame = msg.delete()
+ self.log.debug('openomci-msg', msg=msg)
+ results = yield self.omci_cc.send(frame)
+ self.check_status_and_state(results, 'flow-delete-vlan-tagging-filter-data')
+
+ # Re-Create bridge ani side vlan filter
+ msg = VlanTaggingFilterDataFrame(
+ _mac_bridge_port_ani_entity_id, # Entity ID
+ vlan_tcis=[_set_vlan_vid], # VLAN IDs
+ forward_operation=0x10
+ )
+ frame = msg.create()
+ self.log.debug('openomci-msg', msg=msg)
+ results = yield self.omci_cc.send(frame)
+ self.check_status_and_state(results, 'flow-create-vlan-tagging-filter-data')
+
+ # Update uni side extended vlan filter
+ # filter for untagged
+ # probably for eapol
+ # TODO: magic 0x1000 / 4096?
+ # TODO: lots of magic
+ attributes = dict(
+ received_frame_vlan_tagging_operation_table=
+ VlanTaggingOperation(
+ filter_outer_priority=15,
+ filter_outer_vid=4096,
+ filter_outer_tpid_de=0,
+
+ filter_inner_priority=15,
+ filter_inner_vid=4096,
+ filter_inner_tpid_de=0,
+ filter_ether_type=0,
+
+ treatment_tags_to_remove=0,
+ treatment_outer_priority=15,
+ treatment_outer_vid=0,
+ treatment_outer_tpid_de=0,
+
+ treatment_inner_priority=0,
+ treatment_inner_vid=_set_vlan_vid,
+ treatment_inner_tpid_de=4
+ )
+ )
+ msg = ExtendedVlanTaggingOperationConfigurationDataFrame(
+ _mac_bridge_service_profile_entity_id, # Bridge Entity ID
+ attributes=attributes # See above
+ )
+ frame = msg.set()
+ self.log.debug('openomci-msg', msg=msg)
+ results = yield self.omci_cc.send(frame)
+ self.check_status_and_state(results,
+ 'flow-set-ext-vlan-tagging-op-config-data-untagged')
+
+ # Update uni side extended vlan filter
+ # filter for vlan 0
+ # TODO: lots of magic
+ attributes = dict(
+ received_frame_vlan_tagging_operation_table=
+ VlanTaggingOperation(
+ filter_outer_priority=15, # This entry is not a double-tag rule
+ filter_outer_vid=4096, # Do not filter on the outer VID value
+ filter_outer_tpid_de=0, # Do not filter on the outer TPID field
+
+ filter_inner_priority=8, # Filter on inner vlan
+ filter_inner_vid=0x0, # Look for vlan 0
+ filter_inner_tpid_de=0, # Do not filter on inner TPID field
+ filter_ether_type=0, # Do not filter on EtherType
+
+ treatment_tags_to_remove=1,
+ treatment_outer_priority=15,
+ treatment_outer_vid=0,
+ treatment_outer_tpid_de=0,
+
+ treatment_inner_priority=8, # Add an inner tag and insert this value as the priority
+ treatment_inner_vid=_set_vlan_vid, # use this value as the VID in the inner VLAN tag
+ treatment_inner_tpid_de=4, # set TPID
+ )
+ )
+ msg = ExtendedVlanTaggingOperationConfigurationDataFrame(
+ _mac_bridge_service_profile_entity_id, # Bridge Entity ID
+ attributes=attributes # See above
+ )
+ frame = msg.set()
+ self.log.debug('openomci-msg', msg=msg)
+ results = yield self.omci_cc.send(frame)
+ self.check_status_and_state(results,
+ 'flow-set-ext-vlan-tagging-op-config-data-zero-tagged')
+
+ except Exception as e:
+ self.log.exception('failed-to-install-flow', e=e, flow=flow)
+
+ def get_tx_id(self):
+ self.log.debug('function-entry')
+ self.tx_id += 1
+ return self.tx_id
+
+ def create_interface(self, data):
+ self.log.debug('function-entry', data=data)
+ self._onu_indication = data
+
+ def update_interface(self, data):
+ self.log.debug('function-entry', data=data)
+ self.log.info('Not-Implemented-yet')
+ return
+
+ def remove_interface(self, data):
+ self.log.debug('function-entry', data=data)
+ if isinstance(data, VEnetConfig):
+ onu_device = self.adapter_agent.get_device(self.device_id)
+ ports = self.adapter_agent.get_ports(onu_device.parent_id, Port.ETHERNET_UNI)
+ parent_port_num = None
+ for port in ports:
+ if port.label == data.interface.name:
+ parent_port_num = port.port_no
+ break
+
+ parent_device = self.adapter_agent.get_device(onu_device.parent_id)
+ logical_device_id = parent_device.parent_id
+ assert logical_device_id
+ ## TODO: call remove UniPort method
+ #self.del_uni_port(onu_device, logical_device_id,
+ # data.name, parent_port_num)
+ ## TODO: call remove PonPort method
+ #self.delete_v_ont_ani(data)
+ self.log.info('not-handled-yet')
+
+ def create_gemport(self, data):
+ self.log.debug('function-entry', data=data)
+ self.log.info('create-gemport')
+ gem_portdata = GemportsConfigData()
+ gem_portdata.CopyFrom(data)
+
+ ## TODO: fill in what i have. This needs to be provided from the OLT
+ ## currently its hardcoded/static
+ gemdict = {}
+ gemdict['gemport-id'] = gem_portdata.gemport_id
+ gemdict['encryption'] = gem_portdata.aes_indicator
+ gemdict['tcont-ref'] = int(gem_portdata.tcont_ref)
+ gemdict['name'] = gem_portdata.gemport_id
+ gemdict['traffic-class'] = gem_portdata.traffic_class
+ gemdict['traffic-class'] = gem_portdata.traffic_class
+
+ gem_port = OnuGemPort.create(self, gem_port=gemdict, entity_id=self._pon.next_gem_entity_id)
+
+ self._pon.add_gem_port(gem_port)
+
+ self.log.debug('pon-add-gemport', gem_port=gem_port)
+
+
+ @inlineCallbacks
+ def remove_gemport(self, data):
+ self.log.debug('function-entry', data=data)
+ self.log.info('remove-gemport')
+ gem_port = GemportsConfigData()
+ gem_port.CopyFrom(data)
+ device = self.adapter_agent.get_device(self.device_id)
+ if device.connect_status != ConnectStatus.REACHABLE:
+ self.log.error('device-unreachable')
+ returnValue(None)
+
+ #TODO: Create a remove task that encompasses this
+
+
+
+ def create_tcont(self, tcont_data, traffic_descriptor_data):
+ self.log.debug('function-entry', tcont_data=tcont_data, traffic_descriptor_data=traffic_descriptor_data)
+ self.log.info('create-tcont')
+ tcontdata = TcontsConfigData()
+ tcontdata.CopyFrom(tcont_data)
+
+ ## TODO: fill in what i have. This needs to be provided from the OLT
+ ## currently its hardcoded/static
+ tcontdict = {}
+ tcontdict['alloc-id'] = tcontdata.alloc_id
+ tcontdict['name'] = tcontdata.name
+ tcontdict['vont-ani'] = tcontdata.interface_reference
+
+ ## TODO: Not sure what to do with any of this...
+ tddata = {}
+ tddata['name'] = 'not-sure-td-profile'
+ tddata['fixed-bandwidth'] = "not-sure-fixed"
+ tddata['assured-bandwidth'] = "not-sure-assured"
+ tddata['maximum-bandwidth'] = "not-sure-max"
+ tddata['additional-bw-eligibility-indicator'] = "not-sure-additional"
+
+ td = OnuTrafficDescriptor.create(tddata)
+ tcont = OnuTCont.create(self, tcont=tcontdict, td=td)
+
+ self._pon.add_tcont(tcont)
+
+ self.log.debug('pon-add-tcont', tcont=tcont)
+
+ if tcontdata.interface_reference is not None:
+ self.log.debug('tcont', tcont=tcont.alloc_id)
+ else:
+ self.log.info('recevied-null-tcont-data', tcont=tcont.alloc_id)
+
+ @inlineCallbacks
+ def remove_tcont(self, tcont_data, traffic_descriptor_data):
+ self.log.debug('function-entry', tcont_data=tcont_data, traffic_descriptor_data=traffic_descriptor_data)
+ self.log.info('remove-tcont')
+ device = self.adapter_agent.get_device(self.device_id)
+ if device.connect_status != ConnectStatus.REACHABLE:
+ self.log.error('device-unreachable')
+ returnValue(None)
+
+ #TODO: Create some omci task that encompases this what intended
+
+
+ def create_multicast_gemport(self, data):
+ self.log.debug('function-entry', data=data)
+
+ ## TODO: create objects and populate for later omci calls
+
+
+ @inlineCallbacks
+ def disable(self, device):
+ self.log.debug('function-entry', device=device)
+ try:
+ self.log.info('sending-admin-state-lock-towards-device', device=device)
+
+ #TODO: Create uni lock/unlock omci task
+
+ # Stop up OpenOMCI state machines for this device
+ self._onu_omci_device.stop()
+
+ device = self.adapter_agent.get_device(device.id)
+ # Disable all ports on that device
+ self.adapter_agent.disable_all_ports(self.device_id)
+ parent_device = self.adapter_agent.get_device(device.parent_id)
+ logical_device_id = parent_device.parent_id
+ assert logical_device_id
+ # Mark OF PORT STATE DOWN
+ ports = self.adapter_agent.get_ports(device.id, Port.ETHERNET_UNI)
+ for port in ports:
+ state = OFPPS_LINK_DOWN
+ port_id = 'uni-{}'.format(port.port_no)
+ # TODO: move to UniPort
+ self.update_logical_port(logical_device_id, port_id, state)
+ device.oper_status = OperStatus.UNKNOWN
+ device.connect_status = ConnectStatus.UNREACHABLE
+ self.adapter_agent.update_device(device)
+ except Exception as e:
+ log.exception('exception-in-onu-disable', exception=e)
+
+ @inlineCallbacks
+ def reenable(self, device):
+ self.log.debug('function-entry', device=device)
+ try:
+ self.log.info('sending-admin-state-unlock-towards-device', device=device)
+
+ # Start up OpenOMCI state machines for this device
+ self._onu_omci_device.start()
+
+ #TODO: Create uni lock/unlock omci task
+
+ device = self.adapter_agent.get_device(device.id)
+ # Re-enable the ports on that device
+ self.adapter_agent.enable_all_ports(device.id)
+ parent_device = self.adapter_agent.get_device(device.parent_id)
+ logical_device_id = parent_device.parent_id
+ assert logical_device_id
+ # Mark OF PORT STATE UP
+ ports = self.adapter_agent.get_ports(device.id, Port.ETHERNET_UNI)
+ for port in ports:
+ state = OFPPS_LIVE
+ port_id = 'uni-{}'.format(port.port_no)
+ # TODO: move to UniPort
+ self.update_logical_port(logical_device_id, port_id, state)
+ device.oper_status = OperStatus.ACTIVE
+ device.connect_status = ConnectStatus.REACHABLE
+ self.adapter_agent.update_device(device)
+ except Exception as e:
+ log.exception('exception-in-onu-reenable', exception=e)
+
+ @inlineCallbacks
+ def reboot(self):
+ self.log.debug('function-entry')
+ self.log.info('reboot-device')
+ device = self.adapter_agent.get_device(self.device_id)
+ if device.connect_status != ConnectStatus.REACHABLE:
+ self.log.error("device-unreacable")
+ returnValue(None)
+
+ #TODO: Create reboot omci task
+
+ response = None
+ if response is not None:
+ omci_response = response.getfieldval("omci_message")
+ success_code = omci_response.getfieldval("success_code")
+ if success_code == 0:
+ self.log.info("reboot-command-processed-successfully")
+ # Update the device connection and operation status
+ device = self.adapter_agent.get_device(self.device_id)
+ device.connect_status = ConnectStatus.UNREACHABLE
+ device.oper_status = OperStatus.DISCOVERED
+ self.adapter_agent.update_device(device)
+ self.disable_ports(device)
+ else:
+ self.log.info("reboot-failed", success_code=success_code)
+ else:
+ self.log.info("error-in-processing-reboot-response")
+
+ def disable_ports(self, onu_device):
+ self.log.debug('function-entry', onu_device=onu_device)
+ self.log.info('disable-ports', device_id=self.device_id)
+
+ # Disable all ports on that device
+ self.adapter_agent.disable_all_ports(self.device_id)
+
+ parent_device = self.adapter_agent.get_device(onu_device.parent_id)
+ assert parent_device
+ logical_device_id = parent_device.parent_id
+ assert logical_device_id
+ ports = self.adapter_agent.get_ports(onu_device.id, Port.ETHERNET_UNI)
+ for port in ports:
+ port_id = 'uni-{}'.format(port.port_no)
+ # TODO: move to UniPort
+ self.update_logical_port(logical_device_id, port_id, OFPPS_LINK_DOWN)
+
+ def enable_ports(self, onu_device):
+ self.log.debug('function-entry', onu_device=onu_device)
+ self.log.info('enable-ports', device_id=self.device_id)
+
+ # Disable all ports on that device
+ self.adapter_agent.enable_all_ports(self.device_id)
+
+ parent_device = self.adapter_agent.get_device(onu_device.parent_id)
+ assert parent_device
+ logical_device_id = parent_device.parent_id
+ assert logical_device_id
+ ports = self.adapter_agent.get_ports(onu_device.id, Port.ETHERNET_UNI)
+ for port in ports:
+ port_id = 'uni-{}'.format(port.port_no)
+ # TODO: move to UniPort
+ self.update_logical_port(logical_device_id, port_id, OFPPS_LIVE)
+
+
+ def _subscribe_to_events(self):
+ self.log.debug('function-entry')
+
+ # OMCI MIB Database sync status
+ bus = self._onu_omci_device.event_bus
+ topic = OnuDeviceEntry.event_bus_topic(self.device_id,
+ OnuDeviceEvents.MibDatabaseSyncEvent)
+ self._in_sync_subscription = bus.subscribe(topic, self.in_sync_handler)
+
+ # OMCI Capabilities
+ bus = self._onu_omci_device.event_bus
+ topic = OnuDeviceEntry.event_bus_topic(self.device_id,
+ OnuDeviceEvents.OmciCapabilitiesEvent)
+ self._capabilities_subscription = bus.subscribe(topic, self.capabilties_handler)
+
+ def _unsubscribe_to_events(self):
+ self.log.debug('function-entry')
+ if self._in_sync_subscription is not None:
+ bus = self._onu_omci_device.event_bus
+ bus.unsubscribe(self._in_sync_subscription)
+ self._in_sync_subscription = None
+
+ def in_sync_handler(self, _topic, msg):
+ self.log.debug('function-entry', _topic=_topic, msg=msg)
+ if self._in_sync_subscription is not None:
+ try:
+ in_sync = msg[IN_SYNC_KEY]
+
+ if in_sync:
+ # Only call this once
+ bus = self._onu_omci_device.event_bus
+ bus.unsubscribe(self._in_sync_subscription)
+ self._in_sync_subscription = None
+
+ # Start up device_info load
+ self.log.debug('running-mib-sync')
+ reactor.callLater(0, self._mib_in_sync)
+
+ except Exception as e:
+ self.log.exception('in-sync', e=e)
+
+ def capabilties_handler(self, _topic, _msg):
+ self.log.debug('function-entry', _topic=_topic, msg=_msg)
+ if self._capabilities_subscription is not None:
+ self.log.debug('capabilities-handler-done')
+
+ def _mib_in_sync(self):
+ self.log.debug('function-entry')
+ if not self._dev_info_loaded:
+ # Here if in sync. But verify first
+
+ omci = self._onu_omci_device
+ in_sync = omci.mib_db_in_sync
+ self.log.info('mib-in-sync', in_sync=in_sync, already_loaded=self._dev_info_loaded)
+
+ device = self.adapter_agent.get_device(self.device_id)
+ device.oper_status = OperStatus.ACTIVE
+ device.connect_status = ConnectStatus.REACHABLE
+ device.reason = 'MIB Synchronization complete'
+ device.vlan = BRDCM_DEFAULT_VLAN
+ # this is done below. why do it twice?
+ #self.adapter_agent.update_device(device)
+
+ omci_dev = self._onu_omci_device
+ config = omci_dev.configuration
+
+ ## TODO: run this sooner somehow...
+ # In Sync, we can register logical ports now. Ideally this could occur on
+ # the first time we received a successful (no timeout) OMCI Rx response.
+ try:
+ ## TODO: this comes back None.. why?
+ vendor = omci.query_mib_single_attribute(OntG.class_id, 0, 'vendor_id')
+ self.log.debug("queryied vendor_id", vendor=vendor)
+
+ parent_device = self.adapter_agent.get_device(device.parent_id)
+
+ parent_adapter_agent = registry('adapter_loader').get_agent(parent_device.adapter)
+ if parent_adapter_agent is None:
+ self.log.error('openolt_adapter_agent-could-not-be-retrieved')
+
+ ani_g = config.ani_g_entities
+ uni_g = config.uni_g_entities
+ pptp = config.pptp_entities
+
+ for key, value in ani_g.iteritems():
+ self.log.debug("discovered-ani", key=key, value=value)
+
+ for key, value in uni_g.iteritems():
+ self.log.debug("discovered-uni", key=key, value=value)
+
+ for key, value in pptp.iteritems():
+ self.log.debug("discovered-pptp-uni", key=key, value=value)
+
+ entity_id = key
+
+ ##TODO: This knowledge is locked away in openolt. and it assumes one onu equals one uni...
+ uni_no_start = platform.mk_uni_port_num(self._onu_indication.intf_id,
+ self._onu_indication.onu_id)
+
+ working_port = self._next_port_number
+ uni_no = uni_no_start + working_port
+ uni_name = "uni-{}".format(uni_no)
+
+ mac_bridge_port_num = working_port + 1
+
+ self.log.debug('live-port-number-ready', uni_no=uni_no, uni_name=uni_name)
+
+ uni_port = UniPort.create(self, uni_name, uni_no, uni_name, device.vlan, device.vlan)
+ uni_port.entity_id = entity_id
+ uni_port.enabled = True
+ uni_port.mac_bridge_port_num = mac_bridge_port_num
+ uni_port.add_logical_port(uni_port.port_number, subscriber_vlan=device.vlan)
+
+ self.log.debug("created-uni-port", uni=uni_port)
+
+ self._unis[uni_port.port_number] = uni_port
+ self.adapter_agent.add_port(device.id, uni_port.get_port())
+ parent_adapter_agent.add_port(device.parent_id, uni_port.get_port())
+
+ ## TODO: this should be in the PonPortclass
+ pon_port = self._pon.get_port()
+ self.adapter_agent.delete_port_reference_from_parent(self.device_id,
+ pon_port)
+
+ pon_port.peers.extend([Port.PeerPort(device_id=device.parent_id,
+ port_no=uni_port.port_number)])
+
+ self._pon._port = pon_port
+
+ self.adapter_agent.add_port_reference_to_parent(self.device_id,
+ pon_port)
+
+ #TODO: only one uni/pptp for now. flow bug in openolt
+ break
+
+ self._total_tcont_count = ani_g.get('total-tcont-count')
+ self._qos_flexibility = config.qos_configuration_flexibility or 0
+ self._omcc_version = config.omcc_version or OMCCVersion.Unknown
+ self.log.debug("set-total-tcont-count", tcont_count=self._total_tcont_count)
+
+ # TODO: figure out what this is for
+ host_info = omci_dev.query_mib(IpHostConfigData.class_id)
+ mgmt_mac_address = next((host_info[inst].get('attributes').get('mac_address')
+ for inst in host_info
+ if isinstance(inst, int)), 'unknown')
+ device.mac_address = str(mgmt_mac_address)
+ device.model = str(config.version or 'unknown').rstrip('\0')
+
+ equipment_id = config.equipment_id or " unknown unknown "
+ eqpt_boot_version = str(equipment_id).rstrip('\0')
+ boot_version = eqpt_boot_version[12:]
+
+ images = [Image(name='boot-code',
+ version=boot_version.rstrip('\0'),
+ is_active=False,
+ is_committed=True,
+ is_valid=True,
+ install_datetime='Not Available',
+ hash='Not Available')] + \
+ config.software_images
+
+ del (device.images.image[:]) # Clear previous entries
+ device.images.image.extend(images)
+
+ # Save our device information
+ self.adapter_agent.update_device(device)
+
+ # Start MIB download
+ self._in_sync_reached = True
+
+ def success(_results):
+ self.log.debug('function-entry', _results=_results)
+ self.log.info('mib-download-success')
+ self._mib_download_task = None
+
+ def failure(_reason):
+ self.log.debug('function-entry', _reason=_reason)
+ self.log.info('mib-download-failure')
+ self._deferred = reactor.callLater(10, self._mib_download_task)
+
+ self._mib_download_task = BrcmMibDownloadTask(self.omci_agent, self)
+ self._mib_download_deferred = self._onu_omci_device.task_runner.queue_task(self._mib_download_task)
+ self._mib_download_deferred.addCallbacks(success, failure)
+
+ except Exception as e:
+ self.log.exception('device-info-load', e=e)
+ self._deferred = reactor.callLater(_STARTUP_RETRY_WAIT, self._mib_in_sync)
+
+
+ def check_status_and_state(self, results, operation=''):
+ self.log.debug('function-entry')
+ omci_msg = results.fields['omci_message'].fields
+ status = omci_msg['success_code']
+ error_mask = omci_msg.get('parameter_error_attributes_mask', 'n/a')
+ failed_mask = omci_msg.get('failed_attributes_mask', 'n/a')
+ unsupported_mask = omci_msg.get('unsupported_attributes_mask', 'n/a')
+
+ self.log.debug("OMCI Result:", operation, omci_msg=omci_msg, status=status, error_mask=error_mask,
+ failed_mask=failed_mask, unsupported_mask=unsupported_mask)
+
+ if status == RC.Success:
+ return True
+
+ elif status == RC.InstanceExists:
+ return False
+
diff --git a/voltha/adapters/brcm_openomci_onu/omci/__init__.py b/voltha/adapters/brcm_openomci_onu/omci/__init__.py
new file mode 100644
index 0000000..b0fb0b2
--- /dev/null
+++ b/voltha/adapters/brcm_openomci_onu/omci/__init__.py
@@ -0,0 +1,13 @@
+# Copyright 2017-present Open Networking Foundation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
diff --git a/voltha/adapters/brcm_openomci_onu/omci/brcm_capabilities_task.py b/voltha/adapters/brcm_openomci_onu/omci/brcm_capabilities_task.py
new file mode 100644
index 0000000..1c6524a
--- /dev/null
+++ b/voltha/adapters/brcm_openomci_onu/omci/brcm_capabilities_task.py
@@ -0,0 +1,157 @@
+#
+# Copyright 2018 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import structlog
+from voltha.extensions.omci.tasks.onu_capabilities_task import OnuCapabilitiesTask
+from twisted.internet.defer import failure
+
+
+class BrcmCapabilitiesTask(OnuCapabilitiesTask):
+ """
+ OpenOMCI MIB Capabilities Task - BROADCOM ONUs
+
+ This task requests information on supported MEs via the OMCI (ME#287)
+ Managed entity.
+
+ This task should be ran after MIB Synchronization and before any MIB
+ Downloads to the ONU.
+
+ Upon completion, the Task deferred callback is invoked with dictionary
+ containing the supported managed entities and message types.
+
+ results = {
+ 'supported-managed-entities': {set of supported managed entities},
+ 'supported-message-types': {set of supported message types}
+ }
+ """
+ def __init__(self, omci_agent, device_id):
+ """
+ Class initialization
+
+ :param omci_agent: (OmciAdapterAgent) OMCI Adapter agent
+ :param device_id: (str) ONU Device ID
+ """
+ self.log = structlog.get_logger(device_id=device_id)
+ self.log.debug('function-entry')
+
+ super(BrcmCapabilitiesTask, self).__init__(omci_agent, device_id)
+ self._omci_managed = False # TODO: Look up capabilities/model number
+
+ @property
+ def supported_managed_entities(self):
+ """
+ Return a set of the Managed Entity class IDs supported on this ONU
+
+ None is returned if not MEs have been discovered
+
+ :return: (set of ints)
+ """
+ self.log.debug('function-entry')
+
+ if self._omci_managed:
+ return super(BrcmCapabilitiesTask, self).supported_managed_entities
+
+ # TODO: figure out why broadcom wont answer for ME 287 to get this. otherwise manually fill in
+ me_1287800f1 = [
+ 2, 5, 6, 7, 11, 24, 45, 46, 47, 48, 49, 50, 51, 52, 79, 84, 89, 130,
+ 131, 133, 134, 135, 136, 137, 148, 157, 158, 159, 171, 256, 257, 262,
+ 263, 264, 266, 268, 272, 273, 274, 277, 278, 279, 280, 281, 297, 298,
+ 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312,
+ 329, 330, 332, 334, 336, 340, 341, 342, 343, 348, 425, 426, 65300,
+ 65400, 65401, 65402, 65403, 65404, 65406, 65407, 65408, 65409, 65410,
+ 65411, 65412, 65413, 65414, 65420, 65421, 65422, 65423, 65424
+ ]
+ return frozenset(list(me_1287800f1))
+
+ @property
+ def supported_message_types(self):
+ """
+ Return a set of the Message Types supported on this ONU
+
+ None is returned if no message types have been discovered
+
+ :return: (set of EntityOperations)
+ """
+ self.log.debug('function-entry')
+
+ if self._omci_managed:
+ return super(BrcmCapabilitiesTask, self).supported_message_types
+
+ # TODO: figure out why broadcom wont answer for ME 287 to get this. otherwise manually fill in
+ from voltha.extensions.omci.omci_entities import EntityOperations
+ op_11287800f1 = [
+ EntityOperations.Create,
+ EntityOperations.CreateComplete,
+ EntityOperations.Delete,
+ EntityOperations.Set,
+ EntityOperations.Get,
+ EntityOperations.GetComplete,
+ EntityOperations.GetAllAlarms,
+ EntityOperations.GetAllAlarmsNext,
+ EntityOperations.MibUpload,
+ EntityOperations.MibUploadNext,
+ EntityOperations.MibReset,
+ EntityOperations.AlarmNotification,
+ EntityOperations.AttributeValueChange,
+ EntityOperations.Test,
+ EntityOperations.StartSoftwareDownload,
+ EntityOperations.DownloadSection,
+ EntityOperations.EndSoftwareDownload,
+ EntityOperations.ActivateSoftware,
+ EntityOperations.CommitSoftware,
+ EntityOperations.SynchronizeTime,
+ EntityOperations.Reboot,
+ EntityOperations.GetNext,
+ ]
+ return frozenset(op_11287800f1)
+
+ def perform_get_capabilities(self):
+ """
+ Perform the MIB Capabilities sequence.
+
+ The sequence is to perform a Get request with the attribute mask equal
+ to 'me_type_table'. The response to this request will carry the size
+ of (number of get-next sequences).
+
+ Then a loop is entered and get-next commands are sent for each sequence
+ requested.
+ """
+ self.log.debug('function-entry')
+
+ self.log.info('perform-get')
+
+ if self._omci_managed:
+ # Return generator deferred/results
+ return super(BrcmCapabilitiesTask, self).perform_get_capabilities()
+
+ # Fixed values, no need to query
+ try:
+ self._supported_entities = self.supported_managed_entities
+ self._supported_msg_types = self.supported_message_types
+
+ self.log.debug('get-success',
+ supported_entities=self.supported_managed_entities,
+ supported_msg_types=self.supported_message_types)
+ results = {
+ 'supported-managed-entities': self.supported_managed_entities,
+ 'supported-message-types': self.supported_message_types
+ }
+ self.deferred.callback(results)
+
+ except Exception as e:
+ self.log.exception('get-failed', e=e)
+ self.deferred.errback(failure.Failure(e))
+
+
diff --git a/voltha/adapters/brcm_openomci_onu/omci/brcm_get_mds_task.py b/voltha/adapters/brcm_openomci_onu/omci/brcm_get_mds_task.py
new file mode 100644
index 0000000..eabf356
--- /dev/null
+++ b/voltha/adapters/brcm_openomci_onu/omci/brcm_get_mds_task.py
@@ -0,0 +1,61 @@
+#
+# Copyright 2018 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import structlog
+from voltha.extensions.omci.tasks.get_mds_task import GetMdsTask
+
+
+class BrcmGetMdsTask(GetMdsTask):
+ """
+ OpenOMCI Get MIB Data Sync value task - Broadcom ONU
+
+ On successful completion, this task will call the 'callback' method of the
+ deferred returned by the start method and return the value of the MIB
+ Data Sync attribute of the ONT Data ME
+ """
+ name = "BRCM: Get MDS Task"
+
+ def __init__(self, omci_agent, device_id):
+ """
+ Class initialization
+
+ :param omci_agent: (OmciAdapterAgent) OMCI Adapter agent
+ :param device_id: (str) ONU Device ID
+ """
+ self.log = structlog.get_logger(device_id=device_id)
+ self.log.debug('function-entry')
+
+ super(BrcmGetMdsTask, self).__init__(omci_agent, device_id)
+
+ self.name = BrcmGetMdsTask.name
+ self._device = omci_agent.get_device(device_id)
+ self._omci_managed = False # TODO: Look up capabilities/model number/check handler
+
+ def perform_get_mds(self):
+ """
+ Get the 'mib_data_sync' attribute of the ONU
+ """
+ self.log.debug('function-entry')
+ self.log.info('perform-get-mds')
+
+ if self._omci_managed:
+ return super(BrcmGetMdsTask, self).perform_get_mds()
+
+ # Non-OMCI managed BRCM ONUs always return 0 for MDS, use the MIB
+ # sync value and depend on an accelerated mib resync to do the
+ # proper comparison
+
+ self.deferred.callback(self._device.mib_synchronizer.mib_data_sync)
+
diff --git a/voltha/adapters/brcm_openomci_onu/omci/brcm_mib_download_task.py b/voltha/adapters/brcm_openomci_onu/omci/brcm_mib_download_task.py
new file mode 100644
index 0000000..9ac5bca
--- /dev/null
+++ b/voltha/adapters/brcm_openomci_onu/omci/brcm_mib_download_task.py
@@ -0,0 +1,620 @@
+#
+# Copyright 2018 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import structlog
+from common.frameio.frameio import hexify
+from voltha.protos.openflow_13_pb2 import OFPPS_LIVE, OFPPS_LINK_DOWN
+from twisted.internet import reactor
+from twisted.internet.defer import inlineCallbacks, returnValue, TimeoutError, failure
+from voltha.extensions.omci.omci_me import *
+from voltha.extensions.omci.tasks.task import Task
+from voltha.extensions.omci.omci_defs import *
+
+OP = EntityOperations
+RC = ReasonCodes
+
+
+class MibDownloadFailure(Exception):
+ """
+ This error is raised by default when the download fails
+ """
+
+
+class MibResourcesFailure(Exception):
+ """
+ This error is raised by when one or more resources required is not available
+ """
+
+
+class BrcmMibDownloadTask(Task):
+ """
+ OpenOMCI MIB Download Example
+
+ This task takes the legacy OMCI 'script' for provisioning the Broadcom ONU
+ and converts it to run as a Task on the OpenOMCI Task runner. This is
+ in order to begin to decompose service instantiation in preparation for
+ Technology Profile work.
+
+ Once technology profiles are ready, some of this task may hang around or
+ be moved into OpenOMCI if there are any very common settings/configs to do
+ for any profile that may be provided in the v2.0 release
+
+ Currently, the only service tech profiles expected by v2.0 will be for AT&T
+ residential data service and DT residential data service.
+ """
+ task_priority = Task.DEFAULT_PRIORITY + 10
+ default_tpid = 0x8100
+ default_gem_payload = 48
+ BRDCM_DEFAULT_VLAN = 4091
+
+ name = "Broadcom MIB Download Example Task"
+
+ def __init__(self, omci_agent, handler):
+ """
+ Class initialization
+
+ :param omci_agent: (OmciAdapterAgent) OMCI Adapter agent
+ :param device_id: (str) ONU Device ID
+ """
+
+ self.log = structlog.get_logger(device_id=handler.device_id)
+ self.log.debug('function-entry')
+
+ super(BrcmMibDownloadTask, self).__init__(BrcmMibDownloadTask.name,
+ omci_agent,
+ handler.device_id,
+ priority=BrcmMibDownloadTask.task_priority)
+ self._handler = handler
+ self._onu_device = omci_agent.get_device(handler.device_id)
+ self._local_deferred = None
+
+ # Frame size
+ self._max_gem_payload = BrcmMibDownloadTask.default_gem_payload
+
+ # TODO: only using a single UNI/ethernet port
+ self._uni_port = self._handler.uni_ports[0]
+ self._uni_port_num = self._uni_port.mac_bridge_port_num
+ self._ethernet_uni_entity_id = self._uni_port.entity_id
+
+ # Port numbers
+ self._pon_port_num = 3 # TODO why 3. maybe this is the ani port number. look at anis list
+
+ self._input_tpid = BrcmMibDownloadTask.default_tpid
+ self._output_tpid = BrcmMibDownloadTask.default_tpid
+
+ self._vlan_tcis_1 = self.BRDCM_DEFAULT_VLAN
+ self._cvid = self.BRDCM_DEFAULT_VLAN
+ self._vlan_config_entity_id = self._vlan_tcis_1
+
+ # Entity IDs. IDs with values can probably be most anything for most ONUs,
+ # IDs set to None are discovered/set
+
+ # TODO: lots of magic numbers
+ self._mac_bridge_service_profile_entity_id = 0x201
+ self._ieee_mapper_service_profile_entity_id = 0x8001
+ self._mac_bridge_port_ani_entity_id = 0x2102 # TODO: can we just use the entity id from the anis list?
+ self._gal_enet_profile_entity_id = 0x1
+
+ def cancel_deferred(self):
+ self.log.debug('function-entry')
+ super(BrcmMibDownloadTask, self).cancel_deferred()
+
+ d, self._local_deferred = self._local_deferred, None
+ try:
+ if d is not None and not d.called:
+ d.cancel()
+ except:
+ pass
+
+ def start(self):
+ """
+ Start the MIB Download
+ """
+ self.log.debug('function-entry')
+ super(BrcmMibDownloadTask, self).start()
+ self._local_deferred = reactor.callLater(0, self.perform_mib_download)
+
+ def stop(self):
+ """
+ Shutdown MIB Synchronization tasks
+ """
+ self.log.debug('function-entry')
+ self.log.debug('stopping')
+
+ self.cancel_deferred()
+ super(BrcmMibDownloadTask, self).stop()
+
+ def check_status_and_state(self, results, operation=''):
+ """
+ Check the results of an OMCI response. An exception is thrown
+ if the task was cancelled or an error was detected.
+
+ :param results: (OmciFrame) OMCI Response frame
+ :param operation: (str) what operation was being performed
+ :return: True if successful, False if the entity existed (already created)
+ """
+ self.log.debug('function-entry')
+
+ if not self.running:
+ raise MibDownloadFailure('Download Task was cancelled')
+
+ omci_msg = results.fields['omci_message'].fields
+ status = omci_msg['success_code']
+ error_mask = omci_msg.get('parameter_error_attributes_mask', 'n/a')
+ failed_mask = omci_msg.get('failed_attributes_mask', 'n/a')
+ unsupported_mask = omci_msg.get('unsupported_attributes_mask', 'n/a')
+
+ self.log.debug("OMCI Result:", operation, omci_msg=omci_msg, status=status, error_mask=error_mask,
+ failed_mask=failed_mask, unsupported_mask=unsupported_mask)
+
+ if status == RC.Success:
+ return True
+
+ elif status == RC.InstanceExists:
+ return False
+
+ raise MibDownloadFailure('{} failed with a status of {}, error_mask: {}, failed_mask: {}, unsupported_mask: {}'
+ .format(operation, status, error_mask, failed_mask, unsupported_mask))
+
+ @inlineCallbacks
+ def perform_mib_download(self):
+ """
+ Send the commands to minimally configure the PON, Bridge, and
+ UNI ports for this device. The application of any service flows
+ and other characteristics are done as needed.
+ """
+ self.log.debug('function-entry')
+ self.log.info('perform-download')
+
+ device = self._handler.adapter_agent.get_device(self.device_id)
+
+ def resources_available():
+ return (device.vlan > 0 and
+ len(self._handler.uni_ports) > 0 and
+ len(self._handler.pon_port.tconts) and
+ len(self._handler.pon_port.gem_ports))
+
+ if self._handler.enabled and resources_available():
+ device.reason = 'Performing OMCI Download'
+ self._handler.adapter_agent.update_device(device)
+
+ try:
+ # Lock the UNI ports to prevent any alarms during initial configuration
+ # of the ONU
+ yield self.enable_uni(self._uni_port, False)
+
+ # Provision the initial bridge configuration
+ yield self.perform_initial_bridge_setup()
+
+ # And not all the service specific work
+ yield self.perform_service_specific_steps()
+
+ # And re-enable the UNIs if needed
+ yield self.enable_uni(self._uni_port, False)
+
+ # If here, we are done. Set the openflow port live
+ # TODO: move to UniPort
+ self._handler.update_logical_port(self._handler.logical_device_id,
+ self._uni_port.port_id_name(), OFPPS_LIVE)
+ device = self._handler.adapter_agent.get_device(self.device_id)
+
+ device.reason = 'Initial MIB Downloaded'
+ self._handler.adapter_agent.update_device(device)
+ ## TODO: flows should update here... how?
+
+ except TimeoutError as e:
+ self.deferred.errback(failure.Failure(e))
+
+ else:
+ # TODO: Provide better error reason, what was missing...
+ e = MibResourcesFailure('Required resources are not available')
+ self.deferred.errback(failure.Failure(e))
+
+ @inlineCallbacks
+ def perform_initial_bridge_setup(self):
+ self.log.debug('function-entry')
+
+ omci_cc = self._onu_device.omci_cc
+ frame = None
+ ## TODO: too many magic numbers
+
+ try:
+ ########################################################################################
+ # Create GalEthernetProfile - Once per ONU/PON interface
+ #
+ # EntityID will be referenced by:
+ # - GemInterworkingTp
+ # References:
+ # - Nothing
+
+ msg = GalEthernetProfileFrame(
+ self._gal_enet_profile_entity_id,
+ max_gem_payload_size=self._max_gem_payload
+ )
+ frame = msg.create()
+ self.log.debug('openomci-msg', msg=msg)
+ results = yield omci_cc.send(frame)
+ self.check_status_and_state(results, 'create-gal-ethernet-profile')
+
+ ################################################################################
+ # Common - PON and/or UNI #
+ ################################################################################
+ # MAC Bridge Service Profile
+ #
+ # EntityID will be referenced by:
+ # - MAC Bridge Port Configuration Data (PON & UNI)
+ # References:
+ # - Nothing
+
+ # TODO: magic. event if static, assign to a meaningful variable name
+ attributes = {
+ 'spanning_tree_ind': False,
+ 'learning_ind' : True,
+ 'priority' : 0x8000,
+ 'max_age' : 20 * 256,
+ 'hello_time' : 2 * 256,
+ 'forward_delay' : 15 * 256,
+ 'unknown_mac_address_discard' : True
+ }
+ msg = MacBridgeServiceProfileFrame(
+ self._mac_bridge_service_profile_entity_id,
+ attributes
+ )
+ frame = msg.create()
+ self.log.debug('openomci-msg', msg=msg)
+ results = yield omci_cc.send(frame)
+ self.check_status_and_state(results, 'create-mac-bridge-service-profile')
+
+ ################################################################################
+ # PON Specific #
+ ################################################################################
+ # IEEE 802.1 Mapper Service config - Once per PON
+ #
+ # EntityID will be referenced by:
+ # - MAC Bridge Port Configuration Data for the PON port
+ # References:
+ # - Nothing at this point. When a GEM port is created, this entity will
+ # be updated to reference the GEM Interworking TP
+
+ msg = Ieee8021pMapperServiceProfileFrame(self._ieee_mapper_service_profile_entity_id)
+ frame = msg.create()
+ self.log.debug('openomci-msg', msg=msg)
+ results = yield omci_cc.send(frame)
+ self.check_status_and_state(results, 'create-8021p-mapper-service-profile')
+
+ ################################################################################
+ # Create MAC Bridge Port Configuration Data for the PON port via IEEE 802.1
+ # mapper service. Upon receipt by the ONU, the ONU will create an instance
+ # of the following before returning the response.
+ #
+ # - MAC bridge port designation data
+ # - MAC bridge port filter table data
+ # - MAC bridge port bridge table data
+ #
+ # EntityID will be referenced by:
+ # - Implicitly by the VLAN tagging filter data
+ # References:
+ # - MAC Bridge Service Profile (the bridge)
+ # - IEEE 802.1p mapper service profile for PON port
+
+ # TODO: magic. make a static variable for tp_type
+ msg = MacBridgePortConfigurationDataFrame(
+ self._mac_bridge_port_ani_entity_id,
+ bridge_id_pointer=self._mac_bridge_service_profile_entity_id, # Bridge Entity ID
+ port_num=self._pon_port_num, # Port ID ##TODO associated with what?
+ tp_type=3, # TP Type (IEEE 802.1p mapper service)
+ tp_pointer=self._ieee_mapper_service_profile_entity_id # TP ID, 8021p mapper ID
+ )
+ frame = msg.create()
+ self.log.debug('openomci-msg', msg=msg)
+ results = yield omci_cc.send(frame)
+ self.check_status_and_state(results, 'create-mac-bridge-port-configuration-data-part-1')
+
+ ################################################################################
+ # VLAN Tagging Filter config
+ #
+ # EntityID will be referenced by:
+ # - Nothing
+ # References:
+ # - MacBridgePortConfigurationData for the ANI/PON side
+ #
+ # Set anything, this request will not be used when using Extended Vlan
+
+ # TODO: magic. make a static variable for forward_op
+ msg = VlanTaggingFilterDataFrame(
+ self._mac_bridge_port_ani_entity_id, # Entity ID
+ vlan_tcis=[self._vlan_tcis_1], # VLAN IDs
+ forward_operation=0x10
+ )
+ frame = msg.create()
+ self.log.debug('openomci-msg', msg=msg)
+ results = yield omci_cc.send(frame)
+ self.check_status_and_state(results, 'create-vlan-tagging-filter-data')
+
+ ################################################################################
+ # UNI Specific #
+ ################################################################################
+ # MAC Bridge Port config
+ # This configuration is for Ethernet UNI
+ #
+ # EntityID will be referenced by:
+ # - Nothing
+ # References:
+ # - MAC Bridge Service Profile (the bridge)
+ # - PPTP Ethernet UNI
+
+ # TODO: do this for all uni/ports...
+ # TODO: magic. make a static variable for tp_type
+ msg = MacBridgePortConfigurationDataFrame(
+ self._ethernet_uni_entity_id, # Entity ID - This is read-only/set-by-create !!!
+ bridge_id_pointer=self._mac_bridge_service_profile_entity_id, # Bridge Entity ID
+ port_num=self._uni_port_num, # Port ID
+ tp_type=1, # PPTP Ethernet UNI
+ tp_pointer=self._ethernet_uni_entity_id # Ethernet UNI ID
+ )
+ frame = msg.create()
+ self.log.debug('openomci-msg', msg=msg)
+ results = yield omci_cc.send(frame)
+ self.check_status_and_state(results, 'create-mac-bridge-port-configuration-data-part-2')
+
+ except TimeoutError as e:
+ self.log.warn('rx-timeout-1', e=e)
+ raise
+
+ except Exception as e:
+ self.log.exception('omci-setup-1', e=e)
+ raise
+
+ returnValue(None)
+
+ @inlineCallbacks
+ def perform_service_specific_steps(self):
+ self.log.debug('function-entry')
+
+ omci_cc = self._onu_device.omci_cc
+ frame = None
+
+ try:
+ ################################################################################
+ # TCONTS
+ #
+ # EntityID will be referenced by:
+ # - GemPortNetworkCtp
+ # References:
+ # - ONU created TCONT (created on ONU startup)
+
+ tcont_idents = self._onu_device.query_mib(Tcont.class_id)
+ self.log.debug('tcont-idents', tcont_idents=tcont_idents)
+
+ for tcont in self._handler.pon_port.tconts.itervalues():
+ free_entity_id = None
+ for k, v in tcont_idents.items():
+ alloc_check = v.get('attributes', {}).get('alloc_id', 0)
+ # Some onu report both to indicate an available tcont
+ if alloc_check == 0xFF or alloc_check == 0xFFFF:
+ free_entity_id = k
+ break
+ else:
+ free_entity_id = None
+
+ self.log.debug('tcont-loop', free_entity_id=free_entity_id)
+
+ if free_entity_id is None:
+ self.log.error('no-available-tconts')
+ break
+
+ # TODO: Need to restore on failure. Need to check status/results
+ yield tcont.add_to_hardware(omci_cc, free_entity_id)
+
+
+ ################################################################################
+ # GEMS (GemPortNetworkCtp and GemInterworkingTp)
+ #
+ # For both of these MEs, the entity_id is the GEM Port ID. The entity id of the
+ # GemInterworkingTp ME could be different since it has an attribute to specify
+ # the GemPortNetworkCtp entity id.
+ #
+ # for the GemPortNetworkCtp ME
+ #
+ # GemPortNetworkCtp
+ # EntityID will be referenced by:
+ # - GemInterworkingTp
+ # References:
+ # - TCONT
+ # - Hardcoded upstream TM Entity ID
+ # - (Possibly in Future) Upstream Traffic descriptor profile pointer
+ #
+ # GemInterworkingTp
+ # EntityID will be referenced by:
+ # - Ieee8021pMapperServiceProfile
+ # References:
+ # - GemPortNetworkCtp
+ # - Ieee8021pMapperServiceProfile
+ # - GalEthernetProfile
+ #
+
+ for gem_port in self._handler.pon_port.gem_ports.itervalues():
+ tcont = gem_port.tcont
+ if tcont is None:
+ self.log.error('unknown-tcont-reference', gem_id=gem_port.gem_id)
+ continue
+
+ # TODO: Need to restore on failure. Need to check status/results
+ yield gem_port.add_to_hardware(omci_cc,
+ tcont.entity_id,
+ self._ieee_mapper_service_profile_entity_id,
+ self._gal_enet_profile_entity_id)
+
+ ################################################################################
+ # Update the IEEE 802.1p Mapper Service Profile config
+ #
+ # EntityID was created prior to this call. This is a set
+ #
+ # References:
+ # - Gem Interwork TPs are set here
+ #
+ # TODO: All p-bits currently go to the one and only GEMPORT ID for now
+ gem_ports = self._handler.pon_port.gem_ports
+ gem_entity_ids = [gem_port.entity_id for _, gem_port in gem_ports.items()] \
+ if len(gem_ports) else [OmciNullPointer]
+
+ msg = Ieee8021pMapperServiceProfileFrame(
+ self._ieee_mapper_service_profile_entity_id, # 802.1p mapper Service Mapper Profile ID
+ interwork_tp_pointers=gem_entity_ids # Interworking TP IDs
+ )
+ frame = msg.set()
+ self.log.debug('openomci-msg', msg=msg)
+ results = yield omci_cc.send(frame)
+ self.check_status_and_state(results, 'set-8021p-mapper-service-profile')
+
+ ################################################################################
+ # Create Extended VLAN Tagging Operation config (PON-side)
+ #
+ # EntityID relates to the VLAN TCIS
+ # References:
+ # - VLAN TCIS from previously created VLAN Tagging filter data
+ # - PPTP Ethernet UNI
+ #
+
+ # TODO: do this for all uni/ports...
+ ## TODO: magic. static variable for assoc_type
+ attributes = dict(
+ association_type=2, # Assoc Type, PPTP Ethernet UNI
+ associated_me_pointer=self._ethernet_uni_entity_id # Assoc ME, PPTP Entity Id
+ )
+
+ msg = ExtendedVlanTaggingOperationConfigurationDataFrame(
+ self._mac_bridge_service_profile_entity_id, # Bridge Entity ID
+ attributes=attributes
+ )
+ frame = msg.create()
+ self.log.debug('openomci-msg', msg=msg)
+ results = yield omci_cc.send(frame)
+ self.check_status_and_state(results, 'create-extended-vlan-tagging-operation-configuration-data')
+
+ ################################################################################
+ # Update Extended VLAN Tagging Operation Config Data
+ #
+ # Specifies the TPIDs in use and that operations in the downstream direction are
+ # inverse to the operations in the upstream direction
+
+ ## TODO: magic. static variable for downstream_mode
+ attributes = dict(
+ input_tpid=self._input_tpid, # input TPID
+ output_tpid=self._output_tpid, # output TPID
+ downstream_mode=0, # inverse of upstream
+ )
+ msg = ExtendedVlanTaggingOperationConfigurationDataFrame(
+ self._mac_bridge_service_profile_entity_id, # Bridge Entity ID
+ attributes=attributes
+ )
+ frame = msg.set()
+ self.log.debug('openomci-msg', msg=msg)
+ results = yield omci_cc.send(frame)
+ self.check_status_and_state(results, 'set-extended-vlan-tagging-operation-configuration-data')
+
+ ################################################################################
+ # Update Extended VLAN Tagging Operation Config Data
+ #
+ # parameters: Entity Id ( 0x900), Filter Inner Vlan Id(0x1000-4096,do not filter on Inner vid,
+ # Treatment Inner Vlan Id : 2
+
+ # Update uni side extended vlan filter
+ # filter for untagged
+ # probably for eapol
+ # TODO: lots of magic
+ # TODO: magic 0x1000 / 4096?
+ attributes = dict(
+ received_frame_vlan_tagging_operation_table=
+ VlanTaggingOperation(
+ filter_outer_priority=15, # This entry is not a double-tag rule
+ filter_outer_vid=4096, # Do not filter on the outer VID value
+ filter_outer_tpid_de=0, # Do not filter on the outer TPID field
+
+ filter_inner_priority=15,
+ filter_inner_vid=4096,
+ filter_inner_tpid_de=0,
+ filter_ether_type=0,
+
+ treatment_tags_to_remove=0,
+ treatment_outer_priority=15,
+ treatment_outer_vid=0,
+ treatment_outer_tpid_de=0,
+
+ treatment_inner_priority=0,
+ treatment_inner_vid=self._cvid,
+ treatment_inner_tpid_de=4,
+ )
+ )
+ msg = ExtendedVlanTaggingOperationConfigurationDataFrame(
+ self._mac_bridge_service_profile_entity_id, # Bridge Entity ID
+ attributes=attributes # See above
+ )
+ frame = msg.set()
+ self.log.debug('openomci-msg', msg=msg)
+ results = yield omci_cc.send(frame)
+ self.check_status_and_state(results, 'set-extended-vlan-tagging-operation-configuration-data-untagged')
+
+ except TimeoutError as e:
+ self.log.warn('rx-timeout-2', e=e)
+ raise
+
+ except Exception as e:
+ self.log.exception('omci-setup-2', e=e)
+ raise
+
+ returnValue(None)
+
+ @inlineCallbacks
+ def enable_uni(self, uni_port, force_lock):
+ """
+ Lock or unlock a single uni port
+
+ :param uni_port: UniPort to admin up/down
+ :param force_lock: (boolean) If True, force lock regardless of enabled state
+ """
+ self.log.debug('function-entry')
+
+ omci_cc = self._onu_device.omci_cc
+ frame = None
+
+ ################################################################################
+ # Lock/Unlock UNI - 0 to Unlock, 1 to lock
+ #
+ # EntityID is referenced by:
+ # - MAC bridge port configuration data for the UNI side
+ # References:
+ # - Nothing
+ try:
+ state = 1 if force_lock or not uni_port.enabled else 0
+ msg = PptpEthernetUniFrame(uni_port.entity_id,
+ attributes=dict(administrative_state=state))
+ frame = msg.set()
+ self.log.debug('openomci-msg', msg=msg)
+ results = yield omci_cc.send(frame)
+ self.check_status_and_state(results, 'set-pptp-ethernet-uni-lock-restore')
+
+ except TimeoutError as e:
+ self.log.warn('rx-timeout', e=e)
+ raise
+
+ except Exception as e:
+ self.log.exception('omci-failure', e=e)
+ raise
+
+ returnValue(None)
+
+
+
diff --git a/voltha/adapters/brcm_openomci_onu/omci/brcm_mib_sync.py b/voltha/adapters/brcm_openomci_onu/omci/brcm_mib_sync.py
new file mode 100644
index 0000000..23ad925
--- /dev/null
+++ b/voltha/adapters/brcm_openomci_onu/omci/brcm_mib_sync.py
@@ -0,0 +1,70 @@
+#
+# Copyright 2018 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import structlog
+from voltha.extensions.omci.state_machines.mib_sync import MibSynchronizer
+
+log = structlog.get_logger()
+
+class BrcmMibSynchronizer(MibSynchronizer):
+ """
+ OpenOMCI MIB Synchronizer state machine for Broadcom ONUs
+ """
+ # broadcom takes a while to sync. going too often causes errors
+ BRCM_RESYNC_DELAY = 300 # Periodically force a resync
+
+ def __init__(self, agent, device_id, mib_sync_tasks, db,
+ advertise_events=False):
+ """
+ Class initialization
+
+ :param agent: (OpenOmciAgent) Agent
+ :param device_id: (str) ONU Device ID
+ :param db: (MibDbVolatileDict) MIB Database
+ :param mib_sync_tasks: (dict) Tasks to run
+ :param advertise_events: (bool) Advertise events on OpenOMCI Event Bus
+ """
+ self.log = structlog.get_logger(device_id=device_id)
+ self.log.debug('function-entry')
+
+ super(BrcmMibSynchronizer, self).__init__(agent, device_id, mib_sync_tasks, db,
+ advertise_events=advertise_events,
+ # states=MibSynchronizer.DEFAULT_STATES,
+ # transitions=MibSynchronizer.DEFAULT_TRANSITIONS,
+ # initial_state='disabled',
+ # timeout_delay=MibSynchronizer.DEFAULT_TIMEOUT_RETRY,
+ # audit_delay=MibSynchronizer.DEFAULT_AUDIT_DELAY,
+ resync_delay=BrcmMibSynchronizer.BRCM_RESYNC_DELAY)
+ self._omci_managed = False # TODO: Look up model number/check handler
+
+ def on_enter_auditing(self):
+ """
+ Perform a MIB Audit. If our last MIB resync was too long in the
+ past, perform a resynchronization anyway
+ """
+ self.log.debug('function-entry')
+
+ # Is this a model that supports full OMCI management. If so, use standard
+ # forced resync delay
+
+ if not self._omci_managed and self._check_if_mib_data_sync_supported():
+ self._omci_managed = True
+ # Revert to standard timeouts
+ self._resync_delay = MibSynchronizer.DEFAULT_RESYNC_DELAY
+
+ super(BrcmMibSynchronizer, self).on_enter_auditing()
+
+ def _check_if_mib_data_sync_supported(self):
+ return False # TODO: Look up to see if we are/check handler
diff --git a/voltha/adapters/brcm_openomci_onu/onu_gem_port.py b/voltha/adapters/brcm_openomci_onu/onu_gem_port.py
new file mode 100644
index 0000000..b9ed4f2
--- /dev/null
+++ b/voltha/adapters/brcm_openomci_onu/onu_gem_port.py
@@ -0,0 +1,257 @@
+#
+# Copyright 2018 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import structlog
+from common.frameio.frameio import hexify
+from twisted.internet.defer import inlineCallbacks, returnValue
+from voltha.extensions.omci.omci_me import *
+from voltha.extensions.omci.omci_defs import *
+
+RC = ReasonCodes
+
+class OnuGemPort(object):
+ """
+ Broadcom ONU specific implementation
+ """
+ def __init__(self, gem_id, alloc_id, entity_id,
+ encryption=False,
+ omci_transport=False,
+ multicast=False,
+ tcont_ref=None,
+ traffic_class=None,
+ intf_ref=None,
+ untagged=False,
+ name=None,
+ handler=None):
+
+ self.log = structlog.get_logger(device_id=handler.device_id, gem_id=gem_id)
+ self.log.debug('function-entry')
+
+ self.name = name
+ self.gem_id = gem_id
+ self._alloc_id = alloc_id
+ self.tcont_ref = tcont_ref
+ self.intf_ref = intf_ref
+ self.traffic_class = traffic_class
+ self._encryption = encryption
+ self._omci_transport = omci_transport
+ self.multicast = multicast
+ self.untagged = untagged
+ self._handler = handler
+
+ self._pon_id = None
+ self._onu_id = None
+ self._entity_id = entity_id
+
+ # Statistics
+ self.rx_packets = 0
+ self.rx_bytes = 0
+ self.tx_packets = 0
+ self.tx_bytes = 0
+
+
+ def __str__(self):
+ return "GemPort: {}, alloc-id: {}, gem-id: {}".format(self.name,
+ self.alloc_id,
+ self.gem_id)
+ @property
+ def pon_id(self):
+ self.log.debug('function-entry')
+ return self._pon_id
+
+ @pon_id.setter
+ def pon_id(self, pon_id):
+ self.log.debug('function-entry')
+ assert self._pon_id is None or self._pon_id == pon_id, 'PON-ID can only be set once'
+ self._pon_id = pon_id
+
+ @property
+ def onu_id(self):
+ self.log.debug('function-entry')
+ return self._onu_id
+
+ @onu_id.setter
+ def onu_id(self, onu_id):
+ self.log.debug('function-entry', onu_id=onu_id)
+ assert self._onu_id is None or self._onu_id == onu_id, 'ONU-ID can only be set once'
+ self._onu_id = onu_id
+
+ @property
+ def alloc_id(self):
+ self.log.debug('function-entry')
+ if self._alloc_id is None and self._handler is not None:
+ try:
+ self._alloc_id = self._handler.pon_port.tconts.get(self.tcont_ref).get('alloc-id')
+
+ except Exception:
+ pass
+
+ return self._alloc_id
+
+ @property
+ def tcont(self):
+ self.log.debug('function-entry')
+ tcont_item = self._handler.pon_port.tconts.get(self.tcont_ref)
+ return tcont_item
+
+ @property
+ def omci_transport(self):
+ self.log.debug('function-entry')
+ return self._omci_transport
+
+ def to_dict(self):
+ self.log.debug('function-entry')
+ return {
+ 'port-id': self.gem_id,
+ 'alloc-id': self.alloc_id,
+ 'encryption': self._encryption,
+ 'omci-transport': self.omci_transport
+ }
+
+ @property
+ def entity_id(self):
+ self.log.debug('function-entry')
+ return self._entity_id
+
+ @property
+ def encryption(self):
+ self.log.debug('function-entry')
+ return self._encryption
+
+ @encryption.setter
+ def encryption(self, value):
+ self.log.debug('function-entry')
+ assert isinstance(value, bool), 'encryption is a boolean'
+
+ if self._encryption != value:
+ self._encryption = value
+
+ @staticmethod
+ def create(handler, gem_port, entity_id):
+ log = structlog.get_logger(device_id=handler.device_id, gem_port=gem_port, entity_id=entity_id)
+ log.debug('function-entry', gem_port=gem_port, entity_id=entity_id)
+
+ return OnuGemPort(gem_port['gemport-id'],
+ None,
+ entity_id,
+ encryption=gem_port['encryption'], # aes_indicator,
+ tcont_ref=gem_port['tcont-ref'],
+ name=gem_port['name'],
+ traffic_class=gem_port['traffic-class'],
+ handler=handler,
+ untagged=False)
+
+ @inlineCallbacks
+ def add_to_hardware(self, omci,
+ tcont_entity_id,
+ ieee_mapper_service_profile_entity_id,
+ gal_enet_profile_entity_id):
+ self.log.debug('function-entry')
+
+ self.log.debug('add-to-hardware', gem_id=self.gem_id,
+ tcont_entity_id=tcont_entity_id,
+ ieee_mapper_service_profile_entity_id=ieee_mapper_service_profile_entity_id,
+ gal_enet_profile_entity_id=gal_enet_profile_entity_id)
+
+ try:
+ direction = "downstream" if self.multicast else "bi-directional"
+ assert not self.multicast, 'MCAST is not supported yet'
+
+ ## TODO: magic numbers here
+ msg = GemPortNetworkCtpFrame(
+ self.entity_id, # same entity id as GEM port
+ port_id=self.gem_id,
+ tcont_id=tcont_entity_id,
+ direction=direction,
+ ## TODO: This points to the Priority Queue ME. Class #277. Use whats discovered in relation to tcont
+ upstream_tm=0x8001
+ #upstream_tm=0x100
+ )
+ frame = msg.create()
+ self.log.debug('openomci-msg', msg=msg)
+ results = yield omci.send(frame)
+ self.check_status_and_state(results, 'create-gem-port-network-ctp')
+
+ except Exception as e:
+ self.log.exception('gemport-create', e=e)
+ raise
+
+ try:
+ ## TODO: magic numbers here
+ msg = GemInterworkingTpFrame(
+ self.entity_id, # same entity id as GEM port
+ gem_port_network_ctp_pointer=self.entity_id,
+ interworking_option=5, # IEEE 802.1
+ service_profile_pointer=ieee_mapper_service_profile_entity_id,
+ interworking_tp_pointer=0x0,
+ pptp_counter=1,
+ gal_profile_pointer=gal_enet_profile_entity_id
+ )
+ frame = msg.create()
+ self.log.debug('openomci-msg', msg=msg)
+ results = yield omci.send(frame)
+ self.check_status_and_state(results, 'create-gem-interworking-tp')
+
+ except Exception as e:
+ self.log.exception('interworking-create', e=e)
+ raise
+
+ returnValue(results)
+
+ @inlineCallbacks
+ def remove_from_hardware(self, omci):
+ self.log.debug('function-entry', omci=omci)
+ self.log.debug('remove-from-hardware', gem_id=self.gem_id)
+
+ try:
+ msg = GemInterworkingTpFrame(self.entity_id)
+ frame = msg.delete()
+ self.log.debug('openomci-msg', msg=msg)
+ results = yield omci.send(frame)
+ self.check_status_and_state(results, 'delete-gem-port-network-ctp')
+ except Exception as e:
+ self.log.exception('interworking-delete', e=e)
+ raise
+
+ try:
+ msg = GemPortNetworkCtpFrame(self.entity_id)
+ frame = msg.delete()
+ self.log.debug('openomci-msg', msg=msg)
+ results = yield omci.send(frame)
+ self.check_status_and_state(results, 'delete-gem-interworking-tp')
+ except Exception as e:
+ self.log.exception('gemport-delete', e=e)
+ raise
+
+ returnValue(results)
+
+
+ def check_status_and_state(self, results, operation=''):
+ self.log.debug('function-entry')
+ omci_msg = results.fields['omci_message'].fields
+ status = omci_msg['success_code']
+ error_mask = omci_msg.get('parameter_error_attributes_mask', 'n/a')
+ failed_mask = omci_msg.get('failed_attributes_mask', 'n/a')
+ unsupported_mask = omci_msg.get('unsupported_attributes_mask', 'n/a')
+
+ self.log.debug("OMCI Result:", operation, omci_msg=omci_msg, status=status, error_mask=error_mask,
+ failed_mask=failed_mask, unsupported_mask=unsupported_mask)
+
+ if status == RC.Success:
+ return True
+
+ elif status == RC.InstanceExists:
+ return False
+
diff --git a/voltha/adapters/brcm_openomci_onu/onu_tcont.py b/voltha/adapters/brcm_openomci_onu/onu_tcont.py
new file mode 100644
index 0000000..b353a7b
--- /dev/null
+++ b/voltha/adapters/brcm_openomci_onu/onu_tcont.py
@@ -0,0 +1,116 @@
+#
+# Copyright 2018 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import structlog
+from common.frameio.frameio import hexify
+from twisted.internet.defer import inlineCallbacks, returnValue, succeed
+from voltha.extensions.omci.omci_me import *
+from voltha.extensions.omci.omci_defs import *
+
+RC = ReasonCodes
+
+
+class OnuTCont(object):
+ """
+ Broadcom ONU specific implementation
+ """
+ def __init__(self, handler, alloc_id, traffic_descriptor,
+ name=None, vont_ani=None):
+
+ self.log = structlog.get_logger(device_id=handler.device_id, alloc_id=alloc_id)
+ self.log.debug('function-entry')
+
+ self.alloc_id = alloc_id
+ self.traffic_descriptor = traffic_descriptor
+ self.name = name
+ self.vont_ani = vont_ani # (string) reference
+
+ self._handler = handler
+ self._entity_id = None
+
+ @property
+ def entity_id(self):
+ self.log.debug('function-entry')
+ return self._entity_id
+
+ @staticmethod
+ def create(handler, tcont, td):
+ log = structlog.get_logger(tcont=tcont, td=td)
+ log.debug('function-entry', tcont=tcont)
+
+ return OnuTCont(handler,
+ tcont['alloc-id'],
+ td,
+ name=tcont['name'],
+ vont_ani=tcont['vont-ani'])
+
+ @inlineCallbacks
+ def add_to_hardware(self, omci, tcont_entity_id):
+ self.log.debug('function-entry', omci=omci, tcont_entity_id=tcont_entity_id)
+ self.log.debug('add-to-hardware', tcont_entity_id=tcont_entity_id)
+
+ self._entity_id = tcont_entity_id
+
+ try:
+ msg = TcontFrame(self.entity_id, self.alloc_id)
+ frame = msg.set()
+ self.log.debug('openomci-msg', msg=msg)
+ results = yield omci.send(frame)
+ self.check_status_and_state(results, 'set-tcont')
+
+ except Exception as e:
+ self.log.exception('tcont-set', e=e)
+ raise
+
+ returnValue(results)
+
+ @inlineCallbacks
+ def remove_from_hardware(self, omci):
+ self.log.debug('function-entry', omci=omci)
+ self.log.debug('remove-from-hardware', tcont_entity_id=self.entity_id)
+
+ # Release tcont by setting alloc_id=0xFFFF
+ # TODO: magic number, create a named variable
+
+ try:
+ msg = TcontFrame(self.entity_id, 0xFFFF)
+ frame = msg.set()
+ self.log.debug('openomci-msg', msg=msg)
+ results = yield omci.send(frame)
+ self.check_status_and_state(results, 'delete-tcont')
+
+ except Exception as e:
+ self.log.exception('tcont-delete', e=e)
+ raise
+
+ returnValue(results)
+
+
+ def check_status_and_state(self, results, operation=''):
+ self.log.debug('function-entry')
+ omci_msg = results.fields['omci_message'].fields
+ status = omci_msg['success_code']
+ error_mask = omci_msg.get('parameter_error_attributes_mask', 'n/a')
+ failed_mask = omci_msg.get('failed_attributes_mask', 'n/a')
+ unsupported_mask = omci_msg.get('unsupported_attributes_mask', 'n/a')
+
+ self.log.debug("OMCI Result:", operation, omci_msg=omci_msg, status=status, error_mask=error_mask,
+ failed_mask=failed_mask, unsupported_mask=unsupported_mask)
+
+ if status == RC.Success:
+ return True
+
+ elif status == RC.InstanceExists:
+ return False
diff --git a/voltha/adapters/brcm_openomci_onu/onu_traffic_descriptor.py b/voltha/adapters/brcm_openomci_onu/onu_traffic_descriptor.py
new file mode 100644
index 0000000..b6ea4ac
--- /dev/null
+++ b/voltha/adapters/brcm_openomci_onu/onu_traffic_descriptor.py
@@ -0,0 +1,117 @@
+#
+# Copyright 2018 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import structlog
+from voltha.adapters.adtran_olt.xpon.best_effort import BestEffort
+from twisted.internet.defer import inlineCallbacks, returnValue, succeed
+
+
+NONE = 0
+BEST_EFFORT_SHARING = 1
+NON_ASSURED_SHARING = 2 # Should match xpon.py values
+DEFAULT = NONE
+
+
+class OnuTrafficDescriptor(object):
+ """
+ Broadcom ONU specific implementation
+ """
+ def __init__(self, fixed, assured, maximum,
+ additional=DEFAULT,
+ best_effort=None,
+ name=None):
+
+ self.log = structlog.get_logger(fixed=fixed, assured=assured, maximum=maximum, additional=additional)
+ self.log.debug('function-entry')
+
+ self.name = name
+ self.fixed_bandwidth = fixed # bps
+ self.assured_bandwidth = assured # bps
+ self.maximum_bandwidth = maximum # bps
+ self.additional_bandwidth_eligibility = additional
+
+ self.best_effort = best_effort if additional == BEST_EFFORT_SHARING else None
+
+
+ @staticmethod
+ def to_string(value):
+ log = structlog.get_logger()
+ log.debug('function-entry', value=value)
+ return {
+ NON_ASSURED_SHARING: "non-assured-sharing",
+ BEST_EFFORT_SHARING: "best-effort-sharing",
+ NONE: "none"
+ }.get(value, "unknown")
+
+
+ @staticmethod
+ def from_value(value):
+ log = structlog.get_logger()
+ log.debug('function-entry', value=value)
+ return {
+ 0: NONE,
+ 1: BEST_EFFORT_SHARING,
+ 2: NON_ASSURED_SHARING,
+ }.get(value, DEFAULT)
+
+
+ def __str__(self):
+ self.log.debug('function-entry')
+ return "OnuTrafficDescriptor: {}, {}/{}/{}".format(self.name,
+ self.fixed_bandwidth,
+ self.assured_bandwidth,
+ self.maximum_bandwidth)
+
+ def to_dict(self):
+ self.log.debug('function-entry')
+ val = {
+ 'fixed-bandwidth': self.fixed_bandwidth,
+ 'assured-bandwidth': self.assured_bandwidth,
+ 'maximum-bandwidth': self.maximum_bandwidth,
+ 'additional-bandwidth-eligibility': OnuTrafficDescriptor.to_string(self.additional_bandwidth_eligibility)
+ }
+ return val
+
+
+ @staticmethod
+ def create(traffic_disc):
+ log = structlog.get_logger()
+ log.debug('function-entry',traffic_disc=traffic_disc)
+
+ additional = OnuTrafficDescriptor.from_value(
+ traffic_disc['additional-bw-eligibility-indicator'])
+
+ if additional == BEST_EFFORT_SHARING:
+ best_effort = BestEffort(traffic_disc['maximum-bandwidth'],
+ traffic_disc['priority'],
+ traffic_disc['weight'])
+ else:
+ best_effort = None
+
+ return OnuTrafficDescriptor(traffic_disc['fixed-bandwidth'],
+ traffic_disc['assured-bandwidth'],
+ traffic_disc['maximum-bandwidth'],
+ name=traffic_disc['name'],
+ best_effort=best_effort,
+ additional=additional)
+
+ @inlineCallbacks
+ def add_to_hardware(self, omci):
+ self.log.debug('function-entry', omci=omci)
+ results = succeed('TODO: Implement me')
+ returnValue(results)
+
+
+
diff --git a/voltha/adapters/brcm_openomci_onu/pon_port.py b/voltha/adapters/brcm_openomci_onu/pon_port.py
new file mode 100644
index 0000000..d1067fa
--- /dev/null
+++ b/voltha/adapters/brcm_openomci_onu/pon_port.py
@@ -0,0 +1,288 @@
+#
+# Copyright 2018 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import structlog
+from twisted.internet.defer import inlineCallbacks, returnValue
+from voltha.protos.common_pb2 import AdminState, OperStatus
+from voltha.protos.device_pb2 import Port
+
+
+class PonPort(object):
+ """Wraps northbound-port/ANI support for ONU"""
+ # TODO: possibly get from olt
+ MIN_GEM_ENTITY_ID = 0x408
+ MAX_GEM_ENTITY_ID = 0x40F
+
+ def __init__(self, handler, port_no):
+ self.log = structlog.get_logger(device_id=handler.device_id, port_no=port_no)
+ self.log.debug('function-entry')
+
+ self._enabled = False
+ self._valid = True
+ self._handler = handler
+ self._deferred = None
+ self._port = None
+ self._port_number = port_no
+ self._next_entity_id = PonPort.MIN_GEM_ENTITY_ID
+
+ self._admin_state = AdminState.ENABLED
+ self._oper_status = OperStatus.ACTIVE
+
+ self._gem_ports = {} # gem-id -> GemPort
+ self._tconts = {} # alloc-id -> TCont
+
+ def __str__(self):
+ return "PonPort" # TODO: Encode current state
+
+ @staticmethod
+ def create(handler, port_no):
+ log = structlog.get_logger(device_id=handler.device_id, port_no=port_no)
+ log.debug('function-entry')
+ port = PonPort(handler, port_no)
+
+ device = handler.adapter_agent.get_device(handler.device_id)
+
+ port._port = Port(port_no=port.port_number,
+ label='PON port',
+ type=Port.PON_ONU,
+ admin_state=port._admin_state,
+ oper_status=port._oper_status,
+ peers=[])
+
+ return port
+
+ def _start(self):
+ self.log.debug('function-entry')
+ self._cancel_deferred()
+
+ self._admin_state = AdminState.ENABLED
+ self._oper_status = OperStatus.ACTIVE
+ self._update_adapter_agent()
+
+ def _stop(self):
+ self.log.debug('function-entry')
+ self._cancel_deferred()
+
+ self._admin_state = AdminState.DISABLED
+ self._oper_status = OperStatus.UNKNOWN
+ self._update_adapter_agent()
+
+ # TODO: stop h/w sync
+
+ def _cancel_deferred(self):
+ self.log.debug('function-entry')
+ d1, self._deferred = self._deferred, None
+
+ for d in [d1]:
+ try:
+ if d is not None and not d.called:
+ d.cancel()
+ except:
+ pass
+
+ def delete(self):
+ self.log.debug('function-entry')
+ self.enabled = False
+ self._valid = False
+ self._handler = None
+
+ @property
+ def enabled(self):
+ self.log.debug('function-entry')
+ return self._enabled
+
+ @enabled.setter
+ def enabled(self, value):
+ self.log.debug('function-entry')
+ if self._enabled != value:
+ self._enabled = value
+
+ if value:
+ self._start()
+ else:
+ self._stop()
+
+ @property
+ def port_number(self):
+ self.log.debug('function-entry')
+ return self._port_number
+
+ @property
+ def next_gem_entity_id(self):
+ self.log.debug('function-entry')
+ entity_id = self._next_entity_id
+
+ self._next_entity_id = self._next_entity_id + 1
+ if self._next_entity_id > PonPort.MAX_GEM_ENTITY_ID:
+ self._next_entity_id = PonPort.MIN_GEM_ENTITY_ID
+
+ return entity_id
+
+ @property
+ def tconts(self):
+ self.log.debug('function-entry')
+ return self._tconts
+
+ @property
+ def gem_ports(self):
+ self.log.debug('function-entry')
+ return self._gem_ports
+
+ def get_port(self):
+ """
+ Get the VOLTHA PORT object for this port
+ :return: VOLTHA Port object
+ """
+ self.log.debug('function-entry')
+ if self._port is None:
+ device = self._handler.adapter_agent.get_device(self._handler.device_id)
+
+ self._port = Port(port_no=self.port_number,
+ label='PON port',
+ type=Port.PON_ONU,
+ admin_state=self._admin_state,
+ oper_status=self._oper_status,
+ peers=[])
+ return self._port
+
+ def _update_adapter_agent(self):
+ """
+ Update the port status and state in the core
+ """
+ self.log.debug('function-entry')
+ self.log.debug('update-adapter-agent', admin_state=self._admin_state,
+ oper_status=self._oper_status)
+
+ if self._port is not None:
+ self._port.admin_state = self._admin_state
+ self._port.oper_status = self._oper_status
+
+ # adapter_agent add_port also does an update of port status
+ try:
+ self._handler.adapter_agent.add_port(self._handler.device_id, self.get_port())
+ except Exception as e:
+ self.log.exception('update-port', e=e)
+
+ def add_tcont(self, tcont, reflow=False):
+ """
+ Creates/ a T-CONT with the given alloc-id
+
+ :param tcont: (TCont) Object that maintains the TCONT properties
+ :param reflow: (boolean) If true, force add (used during h/w resync)
+ :return: (deferred)
+ """
+ self.log.debug('function-entry')
+
+ if not self._valid:
+ return # Deleting
+
+ if not reflow and tcont.alloc_id in self._tconts:
+ return # already created
+
+ self.log.info('add', tcont=tcont, reflow=reflow)
+ self._tconts[tcont.alloc_id] = tcont
+
+ def update_tcont_td(self, alloc_id, new_td):
+ self.log.debug('function-entry')
+
+ tcont = self._tconts.get(alloc_id)
+
+ if tcont is None:
+ return # not-found
+
+ tcont.traffic_descriptor = new_td
+
+ # TODO: Not yet implemented
+ #TODO: How does this affect ONU tcont settings?
+ #try:
+ # results = yield tcont.add_to_hardware(self._handler.omci)
+ #except Exception as e:
+ # self.log.exception('tcont', tcont=tcont, e=e)
+ # # May occur with xPON provisioning, use hw-resync to recover
+ # results = 'resync needed'
+ # returnValue(results)
+
+ @inlineCallbacks
+ def remove_tcont(self, alloc_id):
+ self.log.debug('function-entry')
+
+ tcont = self._tconts.get(alloc_id)
+
+ if tcont is None:
+ returnValue('nop')
+
+ try:
+ del self._tconts[alloc_id]
+ results = yield tcont.remove_from_hardware(self._handler.openomci.omci_cc)
+ returnValue(results)
+
+ except Exception as e:
+ self.log.exception('delete', e=e)
+ raise
+
+ def gem_port(self, gem_id):
+ self.log.debug('function-entry')
+ return self._gem_ports.get(gem_id)
+
+ @property
+ def gem_ids(self):
+ """Get all GEM Port IDs used by this ONU"""
+ self.log.debug('function-entry')
+ return sorted([gem_id for gem_id, gem in self._gem_ports.items()])
+
+ def add_gem_port(self, gem_port, reflow=False):
+ """
+ Add a GEM Port to this ONU
+
+ :param gem_port: (GemPort) GEM Port to add
+ :param reflow: (boolean) If true, force add (used during h/w resync)
+ :return: (deferred)
+ """
+ self.log.debug('function-entry', gem_port=gem_port)
+
+ if not self._valid:
+ return # Deleting
+
+ if not reflow and gem_port.gem_id in self._gem_ports:
+ return # nop
+
+ self.log.info('add', gem_port=gem_port, reflow=reflow)
+ self._gem_ports[gem_port.gem_id] = gem_port
+
+ @inlineCallbacks
+ def remove_gem_id(self, gem_id):
+ """
+ Remove a GEM Port from this ONU
+
+ :param gem_port: (GemPort) GEM Port to remove
+ :return: deferred
+ """
+ self.log.debug('function-entry', gem_id=gem_id)
+
+ gem_port = self._gem_ports.get(gem_id)
+
+ if gem_port is None:
+ returnValue('nop')
+
+ try:
+ del self._gem_ports[gem_id]
+ results = yield gem_port.remove_from_hardware(self._handler.openomci.omci_cc)
+ returnValue(results)
+
+ except Exception as ex:
+ self.log.exception('gem-port-delete', e=ex)
+ raise
+
+
diff --git a/voltha/adapters/brcm_openomci_onu/uni_port.py b/voltha/adapters/brcm_openomci_onu/uni_port.py
new file mode 100644
index 0000000..28aae8f
--- /dev/null
+++ b/voltha/adapters/brcm_openomci_onu/uni_port.py
@@ -0,0 +1,266 @@
+#
+# Copyright 2018 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import structlog
+from voltha.protos.common_pb2 import OperStatus, AdminState
+from voltha.protos.device_pb2 import Port
+from voltha.protos.openflow_13_pb2 import OFPPF_10GB_FD
+from voltha.core.logical_device_agent import mac_str_to_tuple
+from voltha.protos.logical_device_pb2 import LogicalPort
+from voltha.protos.openflow_13_pb2 import OFPPS_LIVE, OFPPF_FIBER, OFPPS_LINK_DOWN
+from voltha.protos.openflow_13_pb2 import ofp_port
+
+
+class UniPort(object):
+ """Wraps southbound-port(s) support for ONU"""
+ DEFAULT_UNTAGGED_VLAN = 4091
+
+ def __init__(self, handler, name, port_no, ofp_port_no, subscriber_vlan=None,
+ untagged_vlan=None):
+ self.log = structlog.get_logger(device_id=handler.device_id,
+ port_no=port_no)
+ self.log.debug('function-entry')
+ self._enabled = False
+ self._handler = handler
+ self._name = name
+ self._port = None
+ self._port_number = port_no
+ self._ofp_port_no = ofp_port_no # Set at by creator (vENET create)
+ self._logical_port_number = None # Set at time of logical port creation
+ self._subscriber_vlan = subscriber_vlan
+ self._untagged_vlan = untagged_vlan
+ self._entity_id = None # TODO: Use port number from UNI-G entity ID
+ self._mac_bridge_port_num = 0
+
+ self._admin_state = AdminState.ENABLED
+ self._oper_status = OperStatus.ACTIVE
+ # TODO Add state, stats, alarm reference, ...
+ pass
+
+ def __str__(self):
+ return "UniPort: {}:{}".format(self.name, self.port_number)
+
+ @staticmethod
+ def create(handler, name, port_no, ofp_port_no, subscriber_vlan, untagged_vlan):
+ log = structlog.get_logger(device_id=handler.device_id, name=name)
+ log.debug('function-entry')
+ port = UniPort(handler, name, port_no, ofp_port_no, subscriber_vlan, untagged_vlan)
+ return port
+
+ def _start(self):
+ self.log.debug('function-entry')
+ self._cancel_deferred()
+
+ self._admin_state = AdminState.ENABLED
+ self._oper_status = OperStatus.ACTIVE
+
+ self._update_adapter_agent()
+ # TODO: start h/w sync
+ # TODO: Enable the actual physical port?
+ pass
+
+ def _stop(self):
+ self.log.debug('function-entry')
+ self._cancel_deferred()
+
+ self._admin_state = AdminState.DISABLED
+ self._oper_status = OperStatus.UNKNOWN
+
+ self._update_adapter_agent()
+ # TODO: Disable/power-down the actual physical port?
+ pass
+
+ def delete(self):
+ self.log.debug('function-entry')
+ self.enabled = False
+ self._handler = None
+ # TODO: anything else
+
+ def _cancel_deferred(self):
+ self.log.debug('function-entry')
+ pass
+
+ @property
+ def name(self):
+ self.log.debug('function-entry')
+ return self._name
+
+ @property
+ def enabled(self):
+ self.log.debug('function-entry')
+ return self._enabled
+
+ @enabled.setter
+ def enabled(self, value):
+ self.log.debug('function-entry')
+ if self._enabled != value:
+ self._enabled = value
+
+ if value:
+ self._start()
+ else:
+ self._stop()
+
+ @property
+ def mac_bridge_port_num(self):
+ """
+ Port number used when creating MacBridgePortConfigurationDataFrame port number
+ :return: (int) port number
+ """
+ self.log.debug('function-entry')
+ return self._mac_bridge_port_num
+
+ @mac_bridge_port_num.setter
+ def mac_bridge_port_num(self, value):
+ self.log.debug('function-entry')
+ self._mac_bridge_port_num = value
+
+ @property
+ def port_number(self):
+ """
+ Physical device port number
+ :return: (int) port number
+ """
+ self.log.debug('function-entry')
+ return self._port_number
+
+ @property
+ def entity_id(self):
+ """
+ OMCI UNI_G entity ID for port
+ """
+ self.log.debug('function-entry')
+ return self._entity_id
+
+ @entity_id.setter
+ def entity_id(self, value):
+ self.log.debug('function-entry')
+ assert self._entity_id is None, 'Cannot reset the Entity ID'
+ self._entity_id = value
+
+ @property
+ def subscriber_vlan(self):
+ """
+ Subscriber vlan assigned to this UNI
+ :return: (int) subscriber vlan
+ """
+ self.log.debug('function-entry')
+ return self._subscriber_vlan
+
+ @property
+ def logical_port_number(self):
+ """
+ Logical device port number (used as OpenFlow port for UNI)
+ :return: (int) port number
+ """
+ self.log.debug('function-entry')
+ return self._logical_port_number
+
+ def _update_adapter_agent(self):
+ """
+ Update the port status and state in the core
+ """
+ self.log.debug('function-entry')
+ self.log.debug('update-adapter-agent', admin_state=self._admin_state,
+ oper_status=self._oper_status)
+
+ if self._port is not None:
+ self._port.admin_state = self._admin_state
+ self._port.oper_status = self._oper_status
+
+ try:
+ # adapter_agent add_port also does an update of existing port
+ self._handler.adapter_agent.add_port(self._handler.device_id,
+ self.get_port())
+
+ except Exception as e:
+ self.log.exception('update-port', e=e)
+
+ def get_port(self):
+ """
+ Get the VOLTHA PORT object for this port
+ :return: VOLTHA Port object
+ """
+ self.log.debug('function-entry')
+
+ if self._port is None:
+ self._port = Port(port_no=self.port_number,
+ label='Ethernet port',
+ type=Port.ETHERNET_UNI,
+ admin_state=self._admin_state,
+ oper_status=self._oper_status)
+ return self._port
+
+ def port_id_name(self):
+ return 'uni-{}'.format(self._logical_port_number)
+
+ def add_logical_port(self, openflow_port_no, subscriber_vlan=None,
+ capabilities=OFPPF_10GB_FD | OFPPF_FIBER,
+ speed=OFPPF_10GB_FD):
+
+ self.log.debug('function-entry')
+
+ if self._logical_port_number is not None:
+ # delete old logical port if it exists
+ try:
+ port = self._handler.adapter_agent.get_logical_port(self._handler.logical_device_id,
+ self.port_id_name())
+ self._handler.adapter_agent.delete_logical_port(self._handler.logical_device_id, port)
+
+ except Exception as e:
+ # assume this exception was because logical port does not already exist
+ pass
+
+ self._logical_port_number = None
+
+ port_no = openflow_port_no or self._ofp_port_no
+ vlan = subscriber_vlan or self._subscriber_vlan
+
+ if self._logical_port_number is None and port_no is not None:
+ self._logical_port_number = port_no
+ self._subscriber_vlan = vlan
+
+ device = self._handler.adapter_agent.get_device(self._handler.device_id)
+
+ if vlan is not None and device.vlan != vlan:
+ device.vlan = vlan
+ self._handler.adapter_agent.update_device(device)
+
+ # leave the ports down until omci mib download has finished. otherwise flows push before time
+ openflow_port = ofp_port(
+ port_no=port_no,
+ hw_addr=mac_str_to_tuple('08:00:%02x:%02x:%02x:%02x' %
+ ((device.parent_port_no >> 8 & 0xff),
+ device.parent_port_no & 0xff,
+ (port_no >> 8) & 0xff,
+ port_no & 0xff)),
+ name=device.serial_number,
+ config=0,
+ state=OFPPS_LINK_DOWN,
+ curr=capabilities,
+ advertised=capabilities,
+ peer=capabilities,
+ curr_speed=speed,
+ max_speed=speed
+ )
+ self._handler.adapter_agent.add_logical_port(self._handler.logical_device_id,
+ LogicalPort(
+ id=self.port_id_name(),
+ ofp_port=openflow_port,
+ device_id=device.id,
+ device_port_no=self._port_number))
+
+ self.log.debug('logical-port', openflow_port=openflow_port)
+ # TODO: Should we use the UNI object 'name' as the id for OpenFlow?
diff --git a/voltha/adapters/openolt/openolt_device.py b/voltha/adapters/openolt/openolt_device.py
index 4257abe..2eeb7a1 100644
--- a/voltha/adapters/openolt/openolt_device.py
+++ b/voltha/adapters/openolt/openolt_device.py
@@ -503,59 +503,84 @@
# Prepare onu configuration
- # onu initialization, base configuration (bridge setup ...)
- def onu_initialization():
+ ## If we are using the old/current broadcom adapter otherwise use the openomci adapter
+ if onu_device.adapter == 'broadcom_onu':
+ self.log.debug('using-broadcom_onu')
- # FIXME: that's definitely cheating
- if onu_device.adapter == 'broadcom_onu':
+ # onu initialization, base configuration (bridge setup ...)
+ def onu_initialization():
+
onu_adapter_agent.adapter.devices_handlers[onu_device.id] \
- .message_exchange(cvid=DEFAULT_MGMT_VLAN)
+ .message_exchange(cvid=DEFAULT_MGMT_VLAN)
self.log.debug('broadcom-message-exchange-started')
- # tcont creation (onu)
- tcont = TcontsConfigData()
- tcont.alloc_id = platform.mk_alloc_id(onu_indication.onu_id)
+ # tcont creation (onu)
+ tcont = TcontsConfigData()
+ tcont.alloc_id = platform.mk_alloc_id(onu_indication.onu_id)
- # gem port creation
- gem_port = GemportsConfigData()
- gem_port.gemport_id = platform.mk_gemport_id(onu_indication.onu_id)
+ # gem port creation
+ gem_port = GemportsConfigData()
+ gem_port.gemport_id = platform.mk_gemport_id(onu_indication.onu_id)
- # ports creation/update
- def port_config():
+ # ports creation/update
+ def port_config():
- # "v_enet" creation (olt)
+ # "v_enet" creation (olt)
- # add_port update port when it exists
- self.adapter_agent.add_port(
- self.device_id,
- Port(
- port_no=uni_no,
- label=uni_name,
- type=Port.ETHERNET_UNI,
- admin_state=AdminState.ENABLED,
- oper_status=OperStatus.ACTIVE))
+ # add_port update port when it exists
+ self.adapter_agent.add_port(
+ self.device_id,
+ Port(
+ port_no=uni_no,
+ label=uni_name,
+ type=Port.ETHERNET_UNI,
+ admin_state=AdminState.ENABLED,
+ oper_status=OperStatus.ACTIVE))
- # v_enet creation (onu)
+ # v_enet creation (onu)
- venet = VEnetConfig(name=uni_name)
- venet.interface.name = uni_name
- onu_adapter_agent.create_interface(onu_device, venet)
+ venet = VEnetConfig(name=uni_name)
+ venet.interface.name = uni_name
+ onu_adapter_agent.create_interface(onu_device, venet)
- # ONU device status update in the datastore
- def onu_update_oper_status():
- onu_device.oper_status = OperStatus.ACTIVE
- onu_device.connect_status = ConnectStatus.REACHABLE
- self.adapter_agent.update_device(onu_device)
+ # ONU device status update in the datastore
+ def onu_update_oper_status():
+ onu_device.oper_status = OperStatus.ACTIVE
+ onu_device.connect_status = ConnectStatus.REACHABLE
+ self.adapter_agent.update_device(onu_device)
- # FIXME : the asynchronicity has to be taken care of properly
- onu_initialization()
- reactor.callLater(10, onu_adapter_agent.create_tcont,
- device=onu_device, tcont_data=tcont,
- traffic_descriptor_data=None)
- reactor.callLater(11, onu_adapter_agent.create_gemport, onu_device,
- gem_port)
- reactor.callLater(12, port_config)
- reactor.callLater(12, onu_update_oper_status)
+ # FIXME : the asynchronicity has to be taken care of properly
+ onu_initialization()
+ reactor.callLater(10, onu_adapter_agent.create_tcont,
+ device=onu_device, tcont_data=tcont,
+ traffic_descriptor_data=None)
+ reactor.callLater(11, onu_adapter_agent.create_gemport, onu_device,
+ gem_port)
+ reactor.callLater(12, port_config)
+ reactor.callLater(12, onu_update_oper_status)
+
+ elif onu_device.adapter == 'brcm_openomci_onu':
+ self.log.debug('using-brcm_openomci_onu')
+
+ # tcont creation (onu)
+ tcont = TcontsConfigData()
+ tcont.alloc_id = platform.mk_alloc_id(onu_indication.onu_id)
+
+ # gem port creation
+ gem_port = GemportsConfigData()
+ gem_port.gemport_id = platform.mk_gemport_id(onu_indication.onu_id)
+ gem_port.tcont_ref = str(tcont.alloc_id)
+
+ self.log.info('inject-tcont-gem-data-onu-handler', onu_indication=onu_indication,
+ tcont=tcont, gem_port=gem_port)
+
+ onu_adapter_agent.create_interface(onu_device, onu_indication)
+ onu_adapter_agent.create_tcont(onu_device, tcont, traffic_descriptor_data=None)
+ onu_adapter_agent.create_gemport(onu_device, gem_port)
+
+ else:
+ self.log.warn('unsupported-openolt-onu-adapter')
+
else:
self.log.warn('Not-implemented-or-invalid-value-of-oper-state',
diff --git a/voltha/extensions/omci/database/mib_db_ext.py b/voltha/extensions/omci/database/mib_db_ext.py
index 3d1f78c..efaede6 100644
--- a/voltha/extensions/omci/database/mib_db_ext.py
+++ b/voltha/extensions/omci/database/mib_db_ext.py
@@ -546,11 +546,23 @@
instance_id=instance_id, attributes=attributes)
now = self._time_to_string(datetime.utcnow())
- attrs = [MibAttributeData(name=k,
- value=self._attribute_to_string(device_id,
- class_id,
- k,
- v)) for k, v in attributes.items()]
+# attrs = [MibAttributeData(name=k,
+# value=self._attribute_to_string(device_id,
+# class_id,
+# k,
+# v)) for k, v in attributes.items()]
+ attrs = []
+ for k, v in attributes.items():
+ if k == 'serial_number':
+ vendor_id = str(v[0:4])
+ vendor_specific = v[4:]
+ vendor_specific = str(vendor_specific.encode('hex'))
+ str_value=vendor_id+vendor_specific
+ attrs.append(MibAttributeData(name=k, value=str_value))
+ else:
+ str_value = self._attribute_to_string(device_id, class_id, k, v)
+ attrs.append(MibAttributeData(name=k, value=str_value))
+
class_data = MibClassData(class_id=class_id,
instances=[MibInstanceData(instance_id=instance_id,
created=now,
@@ -578,11 +590,23 @@
instance_id=instance_id, attributes=attributes)
now = self._time_to_string(datetime.utcnow())
- attrs = [MibAttributeData(name=k,
- value=self._attribute_to_string(device_id,
- class_id,
- k,
- v)) for k, v in attributes.items()]
+# attrs = [MibAttributeData(name=k,
+# value=self._attribute_to_string(device_id,
+# class_id,
+# k,
+# v)) for k, v in attributes.items()]
+ attrs = []
+ for k, v in attributes.items():
+ if k == 'serial_number':
+ vendor_id = str(v[0:4])
+ vendor_specific = v[4:]
+ vendor_specific = str(vendor_specific.encode('hex'))
+ str_value=vendor_id+vendor_specific
+ attrs.append(MibAttributeData(name=k, value=str_value))
+ else:
+ str_value = self._attribute_to_string(device_id, class_id, k, v)
+ attrs.append(MibAttributeData(name=k, value=str_value))
+
instance_data = MibInstanceData(instance_id=instance_id,
created=now,
modified=now,
@@ -682,6 +706,7 @@
except KeyError:
# Here if the class-id does not yet exist in the database
+ self.log.info("adding-key-not-found", class_id=class_id)
return self._add_new_class(device_id, class_id, instance_id,
attributes)
except Exception as e:
diff --git a/voltha/extensions/omci/onu_configuration.py b/voltha/extensions/omci/onu_configuration.py
index 6010294..2ae5836 100644
--- a/voltha/extensions/omci/onu_configuration.py
+++ b/voltha/extensions/omci/onu_configuration.py
@@ -124,6 +124,7 @@
'_cardholder': None,
'_circuit_pack': None,
'_software': None,
+ '_pptp': None
}
@property
@@ -453,3 +454,34 @@
'management-capability': inst_data[ATTRIBUTES_KEY].get('management_capability', 0)
}
return results if len(results) else None
+
+ @property
+ def pptp_entities(self):
+ """
+ Returns discovered PPTP Ethernet entities. TODO more detail here
+ """
+ pptp = self._get_capability('_pptp', PptpEthernetUni.class_id)
+ results = dict()
+
+ if pptp is not None:
+ for inst, inst_data in pptp.items():
+ if isinstance(inst, int):
+ results[inst] = {
+ 'entity-id': inst,
+ 'expected-type': inst_data[ATTRIBUTES_KEY].get('expected_type', 0),
+ 'sensed-type': inst_data[ATTRIBUTES_KEY].get('sensed_type', 0),
+ 'autodetection-config': inst_data[ATTRIBUTES_KEY].get('autodetection_config', 0),
+ 'ethernet-loopback-config': inst_data[ATTRIBUTES_KEY].get('ethernet_loopback_config', 0),
+ 'administrative-state': inst_data[ATTRIBUTES_KEY].get('administrative_state', 0),
+ 'operational-state': inst_data[ATTRIBUTES_KEY].get('operational_state', 0),
+ 'config-ind': inst_data[ATTRIBUTES_KEY].get('config_ind', 0),
+ 'max-frame-size': inst_data[ATTRIBUTES_KEY].get('max_frame_size', 0),
+ 'dte-dce-ind': inst_data[ATTRIBUTES_KEY].get('dte_dce_ind', 0),
+ 'pause-time': inst_data[ATTRIBUTES_KEY].get('pause_time', 0),
+ 'bridged-ip-ind': inst_data[ATTRIBUTES_KEY].get('bridged_ip_ind', 0),
+ 'arc': inst_data[ATTRIBUTES_KEY].get('arc', 0),
+ 'arc-interval': inst_data[ATTRIBUTES_KEY].get('arc_interval', 0),
+ 'pppoe-filter': inst_data[ATTRIBUTES_KEY].get('ppoe_filter', 0),
+ 'power-control': inst_data[ATTRIBUTES_KEY].get('power_control', 0)
+ }
+ return results if len(results) else None