ADTRAN: Initial support for Technology Profiles
Full deprecation of xPON CLI/NBI
Change-Id: I06f6de8d8cf003a9ae9c9a2f1644d3a656b1845f
diff --git a/voltha/adapters/adtran_olt/adtran_device_handler.py b/voltha/adapters/adtran_olt/adtran_device_handler.py
index 049cd65..3b80acb 100644
--- a/voltha/adapters/adtran_olt/adtran_device_handler.py
+++ b/voltha/adapters/adtran_olt/adtran_device_handler.py
@@ -110,6 +110,7 @@
self._rest_support = None
self._initial_enable_complete = False
self.resource_mgr = None
+ self.tech_profiles = None # dict(): intf_id -> ResourceMgr.TechProfile
# Northbound and Southbound ports
self.northbound_ports = {} # port number -> Port
@@ -1138,7 +1139,7 @@
current_time = time.time()
if current_time < timeout:
self.startup = reactor.callLater(5, self._finish_reboot, timeout,
- previous_oper_status,
+ previous_oper_status,
previous_conn_status)
returnValue(self.startup)
diff --git a/voltha/adapters/adtran_olt/adtran_olt.py b/voltha/adapters/adtran_olt/adtran_olt.py
index 32e271a..21ce715 100644
--- a/voltha/adapters/adtran_olt/adtran_olt.py
+++ b/voltha/adapters/adtran_olt/adtran_olt.py
@@ -52,7 +52,7 @@
self.descriptor = Adapter(
id=self.name,
vendor='ADTRAN, Inc.',
- version='1.34',
+ version='1.35',
config=AdapterConfig(log_level=LogLevel.INFO)
)
log.debug('adtran_olt.__init__', adapter_agent=adapter_agent)
diff --git a/voltha/adapters/adtran_olt/adtran_olt_handler.py b/voltha/adapters/adtran_olt/adtran_olt_handler.py
index 056e5be..f1228a8 100644
--- a/voltha/adapters/adtran_olt/adtran_olt_handler.py
+++ b/voltha/adapters/adtran_olt/adtran_olt_handler.py
@@ -32,6 +32,8 @@
from voltha.protos.common_pb2 import AdminState, OperStatus
from voltha.protos.device_pb2 import ImageDownload, Image
from voltha.protos.openflow_13_pb2 import OFPP_MAX
+from common.tech_profile.tech_profile import *
+from voltha.protos.device_pb2 import Port
class AdtranOltHandler(AdtranDeviceHandler):
@@ -80,13 +82,16 @@
self._downloads = {} # name -> Download obj
self._pio_exception_map = []
+ self.downstream_shapping_supported = True # 1971320F1-ML-4154 and later
+
# FIXME: Remove once we containerize. Only exists to keep BroadCom OpenOMCI ONU Happy
# when it reaches up our rear and tries to yank out a UNI port number
self.platform_class = None
# To keep broadcom ONU happy
from voltha.adapters.adtran_olt.resources.adtranolt_platform import adtran_platform
- self.platform = adtran_platform()
+ self.platform = adtran_platform() # TODO: Remove once tech-profiles & containerization is done !!!
+
def __del__(self):
# OLT Specific things here.
@@ -206,6 +211,17 @@
hash='Not Available')
device['software-images'].append(image)
+ # Update features based on version
+ # Format expected to be similar to: 1971320F1-ML-4154
+
+ running_version = next((image.version for image in device.get('software-images', list())
+ if image.is_active), '').split('-')
+ if len(running_version) > 2:
+ try:
+ self.downstream_shapping_supported = int(running_version[-1]) >= 4154
+ except ValueError:
+ pass
+
except Exception as e:
self.log.exception('dev-info-failure', e=e)
raise
@@ -213,18 +229,19 @@
returnValue(device)
def initialize_resource_manager(self):
- # Initialize the resource manager
+ # Initialize the resource and tech profile managers
extra_args = '--olt_model {}'.format(self.resource_manager_key)
self.resource_mgr = AdtranOltResourceMgr(self.device_id,
self.host_and_port,
extra_args,
self.default_resource_mgr_device_info)
+ self._populate_tech_profile_per_pon_port()
@property
def default_resource_mgr_device_info(self):
class AdtranOltDevInfo(object):
def __init__(self, pon_ports):
- self.technology = "gpon"
+ self.technology = "xgspon"
self.onu_id_start = 0
self.onu_id_end = platform.MAX_ONUS_PER_PON
self.alloc_id_start = platform.MIN_TCONT_ALLOC_ID
@@ -238,6 +255,44 @@
return AdtranOltDevInfo(self.southbound_ports)
+ def _populate_tech_profile_per_pon_port(self):
+ self.tech_profiles = {intf_id: self.resource_mgr.resource_managers[intf_id].tech_profile
+ for intf_id in self.resource_mgr.device_info.intf_ids}
+
+ # Make sure we have as many tech_profiles as there are pon ports on
+ # the device
+ assert len(self.tech_profiles) == self.resource_mgr.device_info.pon_ports
+
+ def get_tp_path(self, intf_id, ofp_port_name):
+ # TODO: Should get Table id form the flow, as of now hardcoded to DEFAULT_TECH_PROFILE_TABLE_ID (64)
+ # 'tp_path' contains the suffix part of the tech_profile_instance path.
+ # The prefix to the 'tp_path' should be set to \
+ # TechProfile.KV_STORE_TECH_PROFILE_PATH_PREFIX by the ONU adapter.
+ return self.tech_profiles[intf_id].get_tp_path(DEFAULT_TECH_PROFILE_TABLE_ID,
+ ofp_port_name)
+
+ def delete_tech_profile_instance(self, intf_id, onu_id, logical_port):
+ # Remove the TP instance associated with the ONU
+ ofp_port_name = self.get_ofp_port_name(intf_id, onu_id, logical_port)
+ tp_path = self.get_tp_path(intf_id, ofp_port_name)
+ return self.tech_profiles[intf_id].delete_tech_profile_instance(tp_path)
+
+ def get_ofp_port_name(self, pon_id, onu_id, logical_port_number):
+ parent_port_no = self.pon_id_to_port_number(pon_id)
+ child_device = self.adapter_agent.get_child_device(self.device_id,
+ parent_port_no=parent_port_no, onu_id=onu_id)
+ if child_device is None:
+ self.log.error("could-not-find-child-device", parent_port_no=pon_id, onu_id=onu_id)
+ return None, None
+
+ ports = self.adapter_agent.get_ports(child_device.id, Port.ETHERNET_UNI)
+ port = next((port for port in ports if port.port_no == logical_port_number), None)
+ logical_port = self.adapter_agent.get_logical_port(self.logical_device_id,
+ port.label)
+ ofp_port_name = (logical_port.ofp_port.name, logical_port.ofp_port.port_no)
+
+ return ofp_port_name
+
@inlineCallbacks
def enumerate_northbound_ports(self, device):
"""
@@ -250,7 +305,7 @@
from net.rcmd import RCmd
try:
# Also get the MAC Address for the OLT
- command = "ip -o link | grep eth0 | sed -n -e 's/^.*ether //p' | awk '{ print $1 }'"
+ command = "ip link | grep -A1 eth0 | sed -n -e 's/^.*ether //p' | awk '{ print $1 }'"
rcmd = RCmd(self.ip_address, self.netconf_username, self.netconf_password,
command)
address = yield rcmd.execute()
@@ -392,7 +447,7 @@
pon_id = pon.get('pon-id')
assert pon_id is not None, 'PON ID not found'
if pon['ifIndex'] is None:
- pon['port_no'] = self._pon_id_to_port_number(pon_id)
+ pon['port_no'] = self.pon_id_to_port_number(pon_id)
else:
pass # Need to adjust ONU numbering !!!!
@@ -622,11 +677,11 @@
# TODO: Verify that ONU object cleanup of ONU will also clean
# up logical id and physical port
- pon_intf_id_onu_id = (proxy_address.channel_id,
- proxy_address.onu_id)
+ # pon_intf_id_onu_id = (proxy_address.channel_id,
+ # proxy_address.onu_id)
# Free any PON resources that were reserved for the ONU
- self.resource_mgr.free_pon_resources_for_onu(pon_intf_id_onu_id)
+ # TODO: Done in onu delete now -> self.resource_mgr.free_pon_resources_for_onu(pon_intf_id_onu_id)
def rx_pa_packet(self, packets):
if self._pon_agent is not None:
@@ -988,7 +1043,7 @@
return pon_id, onu_id
- def _pon_id_to_port_number(self, pon_id):
+ def pon_id_to_port_number(self, pon_id):
return pon_id + 1 + 4 # Skip over uninitialized ports
def _port_number_to_pon_id(self, port):
@@ -1232,7 +1287,7 @@
self.adapter_agent.update_device(device)
return done
- def add_onu_device(self, intf_id, onu_id, serial_number, tconts, gem_ports):
+ def add_onu_device(self, pon_id, onu_id, serial_number):
onu_device = self.adapter_agent.get_child_device(self.device_id,
serial_number=serial_number)
if onu_device is not None:
@@ -1240,84 +1295,51 @@
try:
from voltha.protos.voltha_pb2 import Device
-
- # NOTE - channel_id of onu is set to intf_id
+ # NOTE - channel_id of onu is set to pon_id
proxy_address = Device.ProxyAddress(device_id=self.device_id,
- channel_id=intf_id, onu_id=onu_id,
+ channel_id=pon_id, onu_id=onu_id,
onu_session_id=onu_id)
- self.log.debug("added-onu", port_no=intf_id,
+ self.log.debug("added-onu", port_no=pon_id,
onu_id=onu_id, serial_number=serial_number,
proxy_address=proxy_address)
self.adapter_agent.add_onu_device(
parent_device_id=self.device_id,
- parent_port_no=intf_id,
+ parent_port_no=pon_id,
vendor_id=serial_number[:4],
proxy_address=proxy_address,
root=True,
serial_number=serial_number,
admin_state=AdminState.ENABLED,
)
- assert serial_number is not None, 'ONU does not have a serial number'
-
- onu_device = self.adapter_agent.get_child_device(self.device_id,
- serial_number=serial_number)
-
- reactor.callLater(0, self._seba_xpon_create, onu_device, intf_id, onu_id, tconts, gem_ports)
- return onu_device
except Exception as e:
self.log.exception('onu-activation-failed', e=e)
return None
- def _seba_xpon_create(self, onu_device, intf_id, onu_id, tconts, gem_ports):
- # For SEBA, send over tcont and gem_port information until tech profiles are ready
- # tcont creation (onu)
- from voltha.registry import registry
- self.log.info('inject-tcont-gem-data-onu-handler', intf_id=intf_id,
- onu_id=onu_id, tconts=tconts, gem_ports=gem_ports)
+ def setup_onu_tech_profile(self, pon_id, onu_id, logical_port_number):
+ # Send ONU Adapter related tech profile information.
+ self.log.debug('add-tech-profile-info')
- onu_adapter_agent = registry('adapter_loader').get_agent(onu_device.adapter)
- if onu_adapter_agent is None:
- self.log.error('onu_adapter_agent-could-not-be-retrieved',
- onu_device=onu_device)
+ uni_id = self.platform.uni_id_from_uni_port(logical_port_number)
+ parent_port_no = self.pon_id_to_port_number(pon_id)
+ onu_device = self.adapter_agent.get_child_device(self.device_id,
+ onu_id=onu_id,
+ parent_port_no=parent_port_no)
- # In the OpenOLT version, this is where they signal the BroadCom ONU
- # to enable OMCI. It is a call to the create_interface IAdapter method
- #
- # For the Adtran ONU, we are already running OpenOMCI. On the Adtran ONU, a call
- # to create_interface vectors into the xpon_create call which should not
- # recognize the type and raise a value assertion
- try:
- onu_adapter_agent.create_interface(onu_device,
- OnuIndication(intf_id, onu_id))
- except Exception as _e:
- pass
- try:
- # TODO: deprecate the xPON TCONT/TD/GEMPort once we do not have to call into ONU
- last_alloc_id = None
- for tcont_dict in tconts:
- tcont = tcont_dict['object']
- td = tcont.traffic_descriptor.data if tcont.traffic_descriptor is not None else None
- onu_adapter_agent.create_tcont(onu_device, tcont.data, traffic_descriptor_data=td)
- last_alloc_id = tcont.alloc_id
+ ofp_port_name, ofp_port_no = self.get_ofp_port_name(pon_id, onu_id,
+ logical_port_number)
+ if ofp_port_name is None:
+ self.log.error("port-name-not-found")
+ return
- for gem_port_dict in gem_ports:
- gem_port = gem_port_dict['object']
- if onu_device.adapter.lower() == 'brcm_openomci_onu':
- # BroadCom OpenOMCI ONU adapter uses the tcont alloc_id as the tcont_ref and currently
- # they only assign one.
- gem_port.data.tcont_ref = str(last_alloc_id)
- onu_adapter_agent.create_gemport(onu_device, gem_port.data)
- except Exception as _e:
- pass
+ tp_path = self.get_tp_path(pon_id, ofp_port_name)
+ self.log.debug('Load-tech-profile-request-to-onu-handler', tp_path=tp_path)
-class OnuIndication(object):
- def __init__(self, intf_id, onu_id):
- self.name = 'Dummy ONU Indication'
- self.intf_id = intf_id
- self.onu_id = onu_id
- self.oper_state = 'up'
- self.admin_state = 'up'
+ msg = {'proxy_address': onu_device.proxy_address, 'uni_id': uni_id,
+ 'event': 'download_tech_profile', 'event_data': tp_path}
+
+ # Send the event message to the ONU adapter
+ self.adapter_agent.publish_inter_adapter_message(onu_device.id, msg)
diff --git a/voltha/adapters/adtran_olt/flow/evc_map.py b/voltha/adapters/adtran_olt/flow/evc_map.py
index dc8cc2d..ee19df9 100644
--- a/voltha/adapters/adtran_olt/flow/evc_map.py
+++ b/voltha/adapters/adtran_olt/flow/evc_map.py
@@ -17,9 +17,11 @@
import structlog
from enum import Enum
from acl import ACL
-from twisted.internet import defer
+from twisted.internet import defer, reactor
from twisted.internet.defer import inlineCallbacks, returnValue, succeed
from ncclient.operations.rpc import RPCError
+from voltha.adapters.openolt.protos import openolt_pb2
+
log = structlog.get_logger()
@@ -74,9 +76,9 @@
self._evc = None
self._new_acls = dict() # ACL Name -> ACL Object (To be installed into h/w)
self._existing_acls = dict() # ACL Name -> ACL Object (Already in H/w)
- self._gem_ids_and_vid = None # { key -> onu-id, value -> tuple(sorted GEM Port IDs, onu_vid) }
self._is_ingress_map = is_ingress_map
self._pon_id = None
+ self._onu_id = None # Remains None if associated with a multicast flow
self._installed = False
self._needs_update = False
self._status_message = None
@@ -100,6 +102,12 @@
self._match_unicast = False
self._match_igmp = False
+ from common.tech_profile.tech_profile import DEFAULT_TECH_PROFILE_TABLE_ID
+ self._tech_profile_id = DEFAULT_TECH_PROFILE_TABLE_ID
+ self._gem_ids_and_vid = None # { key -> onu-id, value -> tuple(sorted GEM Port IDs, onu_vid) }
+ self._upstream_bandwidth = None
+ self._shaper_name = None
+
# ACL logic
self._eth_type = None
self._ip_protocol = None
@@ -185,8 +193,12 @@
return self._pon_id # May be None
@property
- def onu_ids(self):
- return self._gem_ids_and_vid.keys()
+ def onu_id(self):
+ return self._onu_id # May be None if associated with a multicast flow
+
+ # @property
+ # def onu_ids(self):
+ # return self._gem_ids_and_vid.keys()
@property
def gem_ids_and_vid(self):
@@ -217,8 +229,7 @@
xml += '<match-untagged>{}</match-untagged>'.format('true'
if self._match_untagged
else 'false')
- # if self._c_tag is not None:
- # xml += '<ctag>{}</ctag>'.format(self._c_tag)
+
# TODO: The following is not yet supported (and in some cases, not decoded)
# self._men_priority = EVCMap.PriorityOption.INHERIT_PRIORITY
# self._men_pri = 0 # If Explicit Priority
@@ -229,11 +240,6 @@
# self._match_ce_vlan_id = None
# self._match_untagged = True
# self._match_destination_mac_address = None
- # self._eth_type = None
- # self._ip_protocol = None
- # self._ipv4_dst = None
- # self._udp_dst = None
- # self._udp_src = None
return xml
def _ingress_install_xml(self, onu_s_gem_ids_and_vid, acl_list, create):
@@ -344,6 +350,15 @@
self._installed = True
try:
self._cancel_deferred()
+
+ log.info('upstream-bandwidth')
+ try:
+ yield self.update_upstream_flow_bandwidth()
+
+ except Exception as e:
+ log.exception('upstream-bandwidth-failed', name=self.name, e=e)
+ raise
+
map_xml = self._ingress_install_xml(self._gem_ids_and_vid, work_acls.values(),
not is_installed) \
if self._is_ingress_map else self._egress_install_xml()
@@ -359,7 +374,7 @@
else:
self._new_acls.update(work_acls)
- except RPCError as rpc_err: # TODO: Try to catch this before attempting the install
+ except RPCError as rpc_err:
if rpc_err.tag == 'data-exists': # Known race due to bulk-flow operation
pass
@@ -369,6 +384,15 @@
self._new_acls.update(work_acls)
raise
+ # Install any needed shapers
+ if self._installed:
+ try:
+ yield self.update_downstream_flow_bandwidth()
+
+ except Exception as e:
+ log.exception('shaper-install-failed', name=self.name, e=e)
+ raise
+
returnValue(self._installed and self._valid)
def _ingress_remove_xml(self, onus_gem_ids_and_vid):
@@ -413,12 +437,17 @@
if len(dl) > 0:
defer.gatherResults(dl, consumeErrors=True)
+ def _remove_shaper(_):
+ if self._shaper_name is not None:
+ self.update_downstream_flow_bandwidth(remove=True)
+
map_xml = self._ingress_remove_xml(self._gem_ids_and_vid) if self._is_ingress_map \
else self._egress_remove_xml()
d = self._handler.netconf_client.edit_config(map_xml)
d.addCallbacks(_success, _failure)
d.addBoth(_remove_acls)
+ d.addBoth(_remove_shaper)
return d
@inlineCallbacks
@@ -628,48 +657,101 @@
return {'ingress-port': items[1],
'flow-id': items[2].split('.')[0]} if len(items) > 2 else dict()
- def add_gem_port(self, gem_port, reflow=False):
- # TODO: Refactor
- if self._is_ingress_map:
- def gem_ports():
- ports = []
- for gems_and_vids in self._gem_ids_and_vid.itervalues():
- ports.extend(gems_and_vids[0])
- return ports
+ @inlineCallbacks
+ def update_upstream_flow_bandwidth(self):
+ """
+ Upstream flow bandwidth comes from the flow_entry related to this EVC-MAP
+ and if no bandwidth property is found, allow full bandwidth
+ """
+ # all flows should should be on the same PON
+ flow = self._flows.itervalues().next()
+ is_pon = flow.handler.is_pon_port(flow.in_port)
- before = gem_ports()
- self._setup_gem_ids()
- after = gem_ports()
+ if self._is_ingress_map and is_pon:
+ pon_port = flow.handler.get_southbound_port(flow.in_port)
+ if pon_port is None:
+ returnValue('no PON')
- if reflow or len(before) < len(after):
- self._installed = False
- return self.install()
+ session = self._handler.rest_client
+ # TODO: Refactor with tech profiles
+ tconts = None # pon_port.tconts
+ traffic_descriptors = None # pon_port.traffic_descriptors
- return succeed('nop')
+ if traffic_descriptors is None or tconts is None:
+ returnValue('no TDs on PON')
- def remove_gem_port(self, gem_port):
- # TODO: Refactor
- if self._is_ingress_map:
- def gem_ports():
- ports = []
- for gems_and_vids in self._gem_ids_and_vid.itervalues():
- ports.extend(gems_and_vids[0])
- return ports
+ bandwidth = self._upstream_bandwidth or 10000000000
- before = gem_ports()
- self._setup_gem_ids()
- after = gem_ports()
+ if self.pon_id is not None and self.onu_id is not None:
+ name = 'tcont-{}-{}-data'.format(self.pon_id, self.onu_id)
+ td = traffic_descriptors.get(name)
+ tcont = tconts.get(name)
- if len(before) > len(after):
- if len(after) == 0:
- return self._remove()
- else:
- self._installed = False
- return self.install()
+ if td is not None and tcont is not None:
+ alloc_id = tcont.alloc_id
+ td.maximum_bandwidth = bandwidth
+ try:
+ results = yield td.add_to_hardware(session)
+ log.debug('td-modify-results', results=results)
- return succeed('nop')
+ except Exception as _e:
+ pass
- def _setup_gem_ids(self):
+ @inlineCallbacks
+ def update_downstream_flow_bandwidth(self, remove=False):
+ """
+ Downstream flow bandwidth is extracted from the related EVC flow_entry
+ bandwidth property. It is written to this EVC-MAP only if it is found
+ """
+ xml = None
+ results = None
+
+ if remove:
+ name, self._shaper_name = self._shaper_name, None
+ if name is not None:
+ xml = self._shaper_remove_xml(name)
+ else:
+ if self._evc is not None and self._evc.flow_entry is not None \
+ and self._evc.flow_entry.bandwidth is not None:
+ self._shaper_name = self._name
+ xml = self._shaper_install_xml(self._shaper_name,
+ self._evc.flow_entry.bandwidth * 1000) # kbps
+ if xml is not None:
+ try:
+ log.info('downstream-bandwidth', xml=xml, name=self.name, remove=remove)
+ results = yield self._handler.netconf_client.edit_config(xml)
+
+ except RPCError as rpc_err:
+ if rpc_err.tag == 'data-exists':
+ pass
+
+ except Exception as e:
+ log.exception('downstream-bandwidth', name=self.name, remove=remove, e=e)
+ raise
+
+ returnValue(results)
+
+ def _shaper_install_xml(self, name, bandwidth):
+ xml = '<adtn-shaper:shapers xmlns:adtn-shaper="http://www.adtran.com/ns/yang/adtran-traffic-shapers" xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0">' + \
+ ' <adtn-shaper:shaper nc:operation="create">'
+ xml += ' <adtn-shaper:name>{}</adtn-shaper:name>'.format(name)
+ xml += ' <adtn-shaper:enabled>true</adtn-shaper:enabled>'
+ xml += ' <adtn-shaper:rate>{}</adtn-shaper:rate>'.format(bandwidth)
+ xml += ' <adtn-shaper-evc-map:evc-map xmlns:adtn-shaper-evc-map="http://www.adtran.com/ns/yang/adtran-traffic-shaper-evc-maps">{}</adtn-shaper-evc-map:evc-map>'.format(self.name)
+ xml += ' </adtn-shaper:shaper>'
+ xml += '</adtn-shaper:shapers>'
+ return xml
+
+ def _shaper_remove_xml(self, name):
+ xml = '<adtn-shaper:shapers xmlns:adtn-shaper="http://www.adtran.com/ns/yang/adtran-traffic-shapers" xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0">' + \
+ ' <adtn-shaper:shaper nc:operation="delete">'
+ xml += ' <adtn-shaper:name>{}</adtn-shaper:name>'.format(self.name)
+ xml += ' </adtn-shaper:shaper>'
+ xml += '</adtn-shaper:shapers>'
+ return xml
+
+ def _setup_tech_profiles(self):
+ # Set up the TCONT / GEM Ports for this connection (Downstream only of course)
# all flows should have same GEM port setup
flow = self._flows.itervalues().next()
is_pon = flow.handler.is_pon_port(flow.in_port)
@@ -677,11 +759,116 @@
if self._is_ingress_map and is_pon:
pon_port = flow.handler.get_southbound_port(flow.in_port)
- if pon_port is not None:
- self._pon_id = pon_port.pon_id
- self._gem_ids_and_vid = pon_port.gem_ids(flow.logical_port,
- flow.vlan_id,
- flow.is_multicast_flow)
+ if pon_port is None:
+ return
+
+ onu = next((onu for onu in pon_port.onus if onu.logical_port == flow.logical_port), None)
+
+ if onu is None: # TODO: Add multicast support later (self.onu_id == None)
+ return
+
+ self._pon_id = pon_port.pon_id
+ self._onu_id = onu.onu_id
+
+ # Identify or allocate TCONT and GEM Ports. If the ONU has been informed of the
+ # GEM PORTs that belong to it, the tech profiles were already set up by a previous
+ # flows
+ onu_gems = onu.gem_ids(self._tech_profile_id)
+
+ if len(onu_gems) > 0:
+ self._gem_ids_and_vid[onu.onu_id] = (onu_gems, flow.vlan_id)
+ return
+
+ uni_id = self._handler.platform.uni_id_from_uni_port(flow.logical_port)
+ pon_profile = self._handler.tech_profiles[self.pon_id]
+ alloc_id = None
+
+ try:
+ (ofp_port_name, ofp_port_no) = self._handler.get_ofp_port_name(self.pon_id,
+ self.onu_id,
+ flow.logical_port)
+ if ofp_port_name is None:
+ log.error("port-name-not-found")
+ return
+
+ # Check tech profile instance already exists for derived port name
+ tech_profile = pon_profile.get_tech_profile_instance(self._tech_profile_id,
+ ofp_port_name)
+ log.debug('Get-tech-profile-instance-status',
+ tech_profile_instance=tech_profile)
+
+ if tech_profile is None:
+ # create tech profile instance
+ tech_profile = pon_profile.create_tech_profile_instance(self._tech_profile_id,
+ ofp_port_name,
+ self.pon_id)
+ if tech_profile is None:
+ raise Exception('Tech-profile-instance-creation-failed')
+ else:
+ log.debug('Tech-profile-instance-already-exist-for-given port-name',
+ ofp_port_name=ofp_port_name)
+
+ # upstream scheduler
+ us_scheduler = pon_profile.get_us_scheduler(tech_profile)
+
+ # downstream scheduler
+ ds_scheduler = pon_profile.get_ds_scheduler(tech_profile)
+
+ # create Tcont protobuf
+ pb_tconts = pon_profile.get_tconts(tech_profile, us_scheduler, ds_scheduler)
+
+ # create TCONTs & GEM Ports locally
+ for pb_tcont in pb_tconts:
+ from ..xpon.olt_tcont import OltTCont
+ tcont = OltTCont.create(pb_tcont,
+ self._tech_profile_id,
+ self.pon_id,
+ self.onu_id,
+ uni_id,
+ ofp_port_no)
+ if tcont is not None:
+ onu.add_tcont(tcont)
+
+ # Fetch alloc id and gemports from tech profile instance
+ alloc_id = tech_profile.us_scheduler.alloc_id
+
+ onu_gems = [gem.gemport_id for gem in tech_profile.upstream_gem_port_attribute_list]
+
+ for gem in tech_profile.upstream_gem_port_attribute_list:
+ from ..xpon.olt_gem_port import OltGemPort
+ gem_port = OltGemPort.create(self._handler,
+ gem,
+ tech_profile.us_scheduler.alloc_id,
+ self._tech_profile_id,
+ self.pon_id,
+ self.onu_id,
+ uni_id,
+ ofp_port_no)
+ if gem_port is not None:
+ onu.add_gem_port(gem_port)
+ #
+ #
+
+ self._gem_ids_and_vid = {onu.onu_id: (onu_gems, flow.vlan_id)}
+
+ # Send technology profile information to ONU
+ reactor.callLater(0, self._handler.setup_onu_tech_profile, self._pon_id,
+ self.onu_id, flow.logical_port)
+
+ except BaseException as e:
+ log.exception(exception=e)
+
+ # Update the allocated alloc_id and gem_port_id for the ONU/UNI to KV store
+ pon_intf_onu_id = (self.pon_id, self.onu_id, uni_id)
+ resource_manager = self._handler.resource_mgr.resource_managers[self.pon_id]
+
+ resource_manager.update_alloc_ids_for_onu(pon_intf_onu_id, list([alloc_id]))
+ resource_manager.update_gemport_ids_for_onu(pon_intf_onu_id, onu_gems)
+
+ self._handler.resource_mgr.update_gemports_ponport_to_onu_map_on_kv_store(onu_gems,
+ self.pon_id,
+ self.onu_id,
+ uni_id)
def _decode(self, evc):
from evc import EVC
@@ -701,6 +888,9 @@
is_pon = flow.handler.is_pon_port(flow.in_port)
is_uni = flow.handler.is_uni_port(flow.in_port)
+ if flow.bandwidth is not None:
+ self._upstream_bandwidth = flow.bandwidth * 1000000
+
if is_pon or is_uni:
# Preserve CE VLAN tag only if utility VLAN/EVC
self._uni_port = flow.handler.get_port_name(flow.in_port)
@@ -722,7 +912,8 @@
# If no match of VLAN this may be for untagged traffic or upstream and needs to
# match the gem-port vid
- self._setup_gem_ids()
+
+ self._setup_tech_profiles()
# self._match_untagged = flow.vlan_id is None and flow.inner_vid is None
self._c_tag = flow.inner_vid or flow.vlan_id
diff --git a/voltha/adapters/adtran_olt/flow/flow_entry.py b/voltha/adapters/adtran_olt/flow/flow_entry.py
index 04aa9eb..a28367a 100644
--- a/voltha/adapters/adtran_olt/flow/flow_entry.py
+++ b/voltha/adapters/adtran_olt/flow/flow_entry.py
@@ -89,6 +89,7 @@
self._logical_port = None # Currently ONU VID is logical port if not doing xPON
self._is_multicast = False
self._is_acl_flow = False
+ self._bandwidth = None
# A value used to locate possible related flow entries
self.signature = None
@@ -141,6 +142,11 @@
return self.handler.device_id
@property
+ def bandwidth(self):
+ """ Bandwidth in Mbps (if any) """
+ return self._bandwidth
+
+ @property
def flow_direction(self):
return self._flow_direction
@@ -483,19 +489,22 @@
# Downstream flow
log.debug('*** field.type == METADATA', value=field.table_metadata)
- if 0xFFFFFFFF >= field.table_metadata > OFPVID_PRESENT + 4095:
- # Default flows for old-style controller flows
- self.inner_vid = None
-
- elif field.table_metadata > 0xFFFFFFFF:
+ if field.table_metadata > 4095:
# ONOS v1.13.5 or later. c-vid in upper 32-bits
- self.inner_vid = field.table_metadata >> 32
+ vid = field.table_metadata & 0x0FFF
+ if vid > 0:
+ self.inner_vid = vid # CTag is never '0'
- log.debug('*** field.type == METADATA', value=field.table_metadata,
- inner_vid=self.inner_vid)
+ elif field.table_metadata > 0:
+ # Pre-ONOS v1.13.5 (vid without the 4096 offset)
+ self.inner_vid = field.table_metadata
+
else:
# Upstream flow
pass # Not used upstream at this time
+
+ log.debug('*** field.type == METADATA', value=field.table_metadata,
+ inner_vid=self.inner_vid)
else:
log.warn('unsupported-selection-field', type=field.type)
self._status_message = 'Unsupported field.type={}'.format(field.type)
@@ -672,7 +681,7 @@
sig_table = self._handler.downstream_flows.get(self.signature)
flow_table = sig_table.flows if sig_table is not None else None
- if flow_table is None or flow_id not in flow_table:
+ if flow_table is None or flow_id not in flow_table.keys():
returnValue('NOP')
# Remove from flow table and clean up flow table if empty
diff --git a/voltha/adapters/adtran_olt/onu.py b/voltha/adapters/adtran_olt/onu.py
index a8eb44c..a1c3314 100644
--- a/voltha/adapters/adtran_olt/onu.py
+++ b/voltha/adapters/adtran_olt/onu.py
@@ -18,6 +18,7 @@
import structlog
from twisted.internet import reactor, defer
from twisted.internet.defer import inlineCallbacks, returnValue, succeed
+from common.tech_profile.tech_profile import DEFAULT_TECH_PROFILE_TABLE_ID
from adtran_olt_handler import AdtranOltHandler
from net.adtran_rest import RestInvalidResponseCode
@@ -58,9 +59,8 @@
self._resync_flows = False
self._sync_deferred = None # For sync of ONT config to hardware
- # Resources
- self._gem_ports = {} # gem-id -> GemPort
- self._tconts = {} # alloc-id -> TCont
+ self._gem_ports = {} # gem-id -> GemPort
+ self._tconts = {} # alloc-id -> TCont
self._uni_ports = onu_info['uni-ports']
# Provisionable items
@@ -276,15 +276,13 @@
pass
@inlineCallbacks
- def create(self, tconts, gem_ports, reflow=False):
+ def create(self, reflow=False):
"""
Create (or reflow) this ONU to hardware
- :param tconts: (dict) Current TCONT information
- :param gem_ports: (dict) Current GEM Port configuration information
:param reflow: (boolean) Flag, if True, indicating if this is a reflow ONU
information after an unmanaged OLT hardware reboot
"""
- self.log.debug('create', tconts=tconts, gem_ports=gem_ports, reflow=reflow)
+ self.log.debug('create', reflow=reflow)
self._cancel_deferred()
data = json.dumps({'onu-id': self._onu_id,
@@ -317,26 +315,9 @@
self.log.warn('onu-exists-check', pon_id=self.pon_id, onu_id=self.onu_id,
serial_number=self.serial_number)
- # Now set up all TConts & GEM-ports
- for tcont in tconts:
- try:
- _results = yield self.add_tcont(tcont['object'], reflow=reflow)
-
- except Exception as e:
- self.log.exception('add-tcont', tcont=tcont, e=e)
- first_sync = 2 # Expedite first hw-sync
-
- for gem_port in gem_ports:
- try:
- _results = yield self.add_gem_port(gem_port['object'], reflow=reflow)
-
- except Exception as e:
- self.log.exception('add-gem-port', gem_port=gem_port, reflow=reflow, e=e)
- first_sync = 2 # Expedite first hw-sync
-
self._sync_deferred = reactor.callLater(first_sync, self._sync_hardware)
- # Recalculate PON upstream FEC
+ # Recalculate PON upstream FEC
self.pon.upstream_fec_enable = self.pon.any_upstream_fec_enabled
returnValue('created')
@@ -388,6 +369,10 @@
except Exception as e:
self.log.exception('onu-delete', e=e)
+ # Release resource manager resources for this ONU
+ pon_intf_id_onu_id = (self.pon_id, self.onu_id)
+ self._olt.resource_mgr.free_pon_resources_for_onu(pon_intf_id_onu_id)
+
returnValue('deleted')
def start(self):
@@ -404,10 +389,7 @@
self._cancel_deferred()
self._sync_deferred = reactor.callLater(0, self._sync_hardware)
- tconts, self._tconts = self._tconts, {}
- gem_ports, self._gem_ports = self._gem_ports, {}
-
- return self.create(tconts, gem_ports)
+ return self.create()
def _sync_hardware(self):
from codec.olt_config import OltConfig
@@ -646,24 +628,6 @@
except Exception as e:
self.log.exception('tcont', tcont=tcont, reflow=reflow, e=e)
- # May occur with xPON provisioning, use hw-resync to recover
- results = 'resync needed'
-
- returnValue(results)
-
- @inlineCallbacks
- def update_tcont_td(self, alloc_id, new_td):
- tcont = self._tconts.get(alloc_id)
-
- if tcont is None:
- returnValue('not-found')
-
- tcont.traffic_descriptor = new_td
- try:
- results = yield tcont.add_to_hardware(self.olt.rest_client)
- 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)
@@ -693,10 +657,12 @@
def gem_port(self, gem_id):
return self._gem_ports.get(gem_id)
- def gem_ids(self):
+ def gem_ids(self, tech_profile_id):
"""Get all GEM Port IDs used by this ONU"""
+ assert tech_profile_id >= DEFAULT_TECH_PROFILE_TABLE_ID
return sorted([gem_id for gem_id, gem in self._gem_ports.items()
- if not gem.multicast])
+ if not gem.multicast and
+ tech_profile_id == gem.tech_profile_id])
@inlineCallbacks
def add_gem_port(self, gem_port, reflow=False):
@@ -724,28 +690,15 @@
try:
results = yield gem_port.add_to_hardware(self.olt.rest_client)
- # May need to update flow tables/evc-maps
- if gem_port.alloc_id in self._tconts:
- from flow.flow_entry import FlowEntry
- # GEM-IDs are a sorted list (ascending). First gemport handles downstream traffic
- # from flow.flow_entry import FlowEntry
- evc_maps = FlowEntry.find_evc_map_flows(self)
-
- for evc_map in evc_maps:
- evc_map.add_gem_port(gem_port, reflow=reflow)
except Exception as e:
self.log.exception('gem-port', gem_port=gem_port, reflow=reflow, e=e)
- # This can happen with xPON if the ONU has been provisioned, but the PON Discovery
- # has not occurred for the ONU. Rely on hw sync to recover
results = 'resync needed'
returnValue(results)
@inlineCallbacks
def remove_gem_id(self, gem_id):
- from flow.flow_entry import FlowEntry
-
gem_port = self._gem_ports.get(gem_id)
if gem_port is None:
@@ -753,14 +706,6 @@
del self._gem_ports[gem_id]
try:
-
- if gem_port.alloc_id in self._tconts:
- # May need to update flow tables/evc-maps
- # GEM-IDs are a sorted list (ascending). First gemport handles downstream traffic
- evc_maps = FlowEntry.find_evc_map_flows(self)
- for evc_map in evc_maps:
- evc_map.remove_gem_port(gem_port)
-
yield gem_port.remove_from_hardware(self.olt.rest_client)
except RestInvalidResponseCode as e:
@@ -771,13 +716,6 @@
self.log.exception('gem-port-delete', e=ex)
raise
- for evc_map in FlowEntry.find_evc_map_flows(self):
- try:
- evc_map.remove_gem_port(gem_port)
-
- except Exception as ex:
- self.log.exception('evc-map-gem-remove', e=ex)
-
returnValue('done')
@staticmethod
diff --git a/voltha/adapters/adtran_olt/pon_port.py b/voltha/adapters/adtran_olt/pon_port.py
index 6a76fd9..c9e18c8 100644
--- a/voltha/adapters/adtran_olt/pon_port.py
+++ b/voltha/adapters/adtran_olt/pon_port.py
@@ -69,7 +69,7 @@
self._discovered_onus = [] # List of serial numbers
self._discovery_deferred = None # Specifically for ONU discovery
- self._onus = {} # serial_number-base64 -> ONU (allowed list)
+ self._onus = {} # serial_number-base64 -> ONU
self._onu_by_id = {} # onu-id -> ONU
self._mcast_gem_ports = {} # VLAN -> GemPort
@@ -87,13 +87,6 @@
# Statistics
self.tx_bip_errors = 0
- # xPON Configuration (TODO: Move Tcont/GEMPort to ONU eventually)
- # TODO: TD may be system-wide, wait for Service Profiles
- self._v_ont_anis = {} # Name -> dict
- self._ont_anis = {} # Name -> dict
- self._tconts = {} # Name -> dict
- self._gem_ports = {} # Name -> dict
-
def __str__(self):
return "PonPort-{}: Admin: {}, Oper: {}, OLT: {}".format(self._label,
self._admin_state,
@@ -473,9 +466,10 @@
if logical_port is None or (logical_port == vlan and logical_port in self.olt.multicast_vlans):
gem_ids[vlan] = ([gem_port.gem_id], None)
else:
- for onu_id, onu in self._onu_by_id.iteritems():
- if logical_port is None or logical_port == onu.logical_port:
- gem_ids[onu_id] = (onu.gem_ids(), flow_vlan)
+ raise NotImplemented('TODO: This is deprecated')
+ # for onu_id, onu in self._onu_by_id.iteritems():
+ # if logical_port is None or logical_port == onu.logical_port:
+ # gem_ids[onu_id] = (onu.gem_ids(), flow_vlan)
return gem_ids
def _get_pon_config(self):
@@ -627,11 +621,9 @@
# Get new/missing from the discovered ONU leaf. Stale ONUs from previous
# configs are now cleaned up during h/w re-sync/reflow.
-
new, rediscovered_onus = self._process_status_onu_discovered_list(status.discovered_onu)
# Process newly discovered ONU list and rediscovered ONUs
-
for serial_number in new | rediscovered_onus:
reactor.callLater(0, self.add_onu, serial_number, status)
@@ -725,41 +717,24 @@
def _get_onu_info(self, serial_number):
"""
Parse through available xPON information for ONU configuration settings
+
:param serial_number: (string) Decoded (not base64) serial number string
:return: (dict) onu config data or None on lookup failure
"""
try:
if self.activation_method == "autodiscovery":
# if self.authentication_method == 'serial-number':
- raise NotImplemented('TODO: Not supported at this time')
+ raise NotImplemented('autodiscovery: Not supported at this time')
elif self.activation_method == "autoactivate":
- # TODO: Currently a somewhat copy of the xPON way to do things
- # update here with Technology profile info when it becomes available
- activate_onu, tconts, gem_ports = self.get_device_profile_info(serial_number)
+ onu_id = self.get_next_onu_id
+ enabled = True
+ channel_speed = 10000000000
+ upstream_fec_enabled = True
- try:
- # TODO: (SEBA) All of this can be greatly simplified once we fully
- # deprecate xPON support
- vont_ani = next(info for _, info in self._v_ont_anis.items()
- if info.get('expected-serial-number') == serial_number)
-
- onu_id = vont_ani['onu-id']
- enabled = vont_ani['enabled']
- channel_speed = vont_ani['upstream-channel-speed']
- upstream_fec_enabled = self._ont_anis[vont_ani['name']]['upstream-fec']
-
- except StopIteration:
- # Can happen if vont-ani or ont-ani has not yet been configured
- self.log.error('no-vont-or-ont-autoactivate')
- return None, False
-
- except Exception as e:
- self.log.exception('autoactivate', e=e)
- raise
else:
- self.log.debug('unsupported-activation-method', method=self.activation_method)
- return None, False
+ self.log.error('unsupported-activation-method', method=self.activation_method)
+ return None
onu_info = {
'device-id': self.olt.device_id,
@@ -770,30 +745,26 @@
'upstream-channel-speed': channel_speed,
'upstream-fec': upstream_fec_enabled,
'password': Onu.DEFAULT_PASSWORD,
- 't-conts': tconts,
- 'gem-ports': gem_ports,
}
- intf_id = platform.intf_id_to_port_no(self._pon_id, Port.PON_OLT)
+ pon_id = self.olt.pon_id_to_port_number(self._pon_id)
- # TODO: Currently only one ONU port and it is hardcoded to port 0
- onu_info['uni-ports'] = [platform.mk_uni_port_num(intf_id, onu_id)]
-
- # Hold off ONU activation until at least one GEM Port is defined.
- self.log.debug('onu-info-tech-profiles', gem_ports=gem_ports)
+ # TODO: Currently only one UNI port and it is hardcoded to port 0
+ onu_info['uni-ports'] = [platform.mk_uni_port_num(pon_id, onu_id)]
# return onu_info
- return onu_info, activate_onu
+ return onu_info
except Exception as e:
self.log.exception('get-onu-info-tech-profiles', e=e)
- return None, False
+ return None
@inlineCallbacks
def add_onu(self, serial_number_64, status):
"""
Add an ONU to the PON
- TODO: This needs major refactoring after xPON is deprecated to be more maintainable
+ :param serial_number_64: (str) base-64 encoded serial number
+ :param status: (dict) OLT PON status. Used to detect if ONU is already provisioned
"""
serial_number = Onu.serial_number_to_string(serial_number_64)
self.log.info('add-onu', serial_number=serial_number,
@@ -804,76 +775,53 @@
if serial_number_64 in self._onus:
returnValue('wait-for-fpga')
- onu_info, activate_onu = self._get_onu_info(serial_number)
+ if serial_number_64 in status.onus:
+ # Handles fast entry into this task before FPGA can clear results of ONU delete
+ returnValue('sticky-onu')
- if activate_onu:
- alarm = OnuDiscoveryAlarm(self.olt.alarms, self.pon_id, serial_number)
- reactor.callLater(0, alarm.raise_alarm)
+ # At our limit? TODO: Retrieve from device resource manager if available
+ if len(self._onus) >= self.MAX_ONUS_SUPPORTED:
+ self.log.warning('max-onus-provisioned', count=len(self._onus))
+ returnValue('max-onus-reached')
- if serial_number_64 not in status.onus or onu_info['onu-id'] in self._active_los_alarms:
- onu = None
- onu_id = onu_info['onu-id']
+ onu_info = self._get_onu_info(serial_number)
+ onu_id = onu_info['onu-id']
- if serial_number_64 in self._onus and onu_id in self._onu_by_id:
- # Handles fast entry into this task before FPGA can set/clear results
- returnValue('sticky-onu')
+ if onu_id is None:
+ self.log.warning('no-onu-ids-available', serial_number=serial_number,
+ serial_number_64=serial_number_64)
+ returnValue('no-ids-available')
- elif (serial_number_64 in self._onus and onu_id not in self._onu_by_id) or \
- (serial_number_64 not in self._onus and onu_id in self._onu_by_id):
- # May be here due to unmanaged power-cycle on OLT or fiber bounced for a
- # previously activated ONU.
- #
- # TODO: Track when the ONU was discovered, and if > some maximum amount
- # place the ONU (with serial number & ONU ID) on a wait list and
- # use that to recover the ONU ID should it show up within a
- # reasonable amount of time. Periodically groom the wait list and
- # delete state ONUs so we can reclaim the ONU ID.
- #
- returnValue('waiting-for-fpga') # non-XPON mode will not
+ # TODO: Is the best before or after creation in parent device?
+ alarm = OnuDiscoveryAlarm(self.olt.alarms, self.pon_id, serial_number)
+ reactor.callLater(0, alarm.raise_alarm)
- elif len(self._onus) >= self.MAX_ONUS_SUPPORTED:
- self.log.warning('max-onus-provisioned', count=len(self._onus))
- returnValue('max-onus-reached')
+ # Have the core create the ONU device
+ self._parent.add_onu_device(self._port_no, onu_id, serial_number)
- else:
- # TODO: Make use of upstream_channel_speed variable
- onu = Onu(onu_info)
- self._onus[serial_number_64] = onu
- self._onu_by_id[onu.onu_id] = onu
+ onu = Onu(onu_info)
+ self._onus[serial_number_64] = onu
+ self._onu_by_id[onu.onu_id] = onu
- if onu is not None:
- tconts = onu_info.pop('t-conts')
- gem_ports = onu_info.pop('gem-ports')
+ try:
+ # Add Multicast to PON on a per-ONU basis
+ #
+ # for id_or_vid, gem_port in gem_ports.iteritems():
+ # try:
+ # if gem_port.multicast:
+ # self.log.debug('id-or-vid', id_or_vid=id_or_vid)
+ # vid = self.olt.multicast_vlans[0] if len(self.olt.multicast_vlans) else None
+ # if vid is not None:
+ # self.add_mcast_gem_port(gem_port, vid)
+ #
+ # except Exception as e:
+ # self.log.exception('id-or-vid', e=e)
- if activate_onu:
- _onu_device = self._parent.add_onu_device(self._port_no, # PON ID
- onu.onu_id, # ONU ID
- serial_number,
- tconts,
- gem_ports)
- try:
- # Add Multicast to PON on a per-ONU basis until xPON multicast support
- # is ready
- # In xPON/BBF, mcast gems tie back to the channel-pair
- # MCAST VLAN IDs stored as a negative value
+ _results = yield onu.create()
- # for id_or_vid, gem_port in gem_ports.iteritems(): # TODO: Deprecate this when BBF ready
- # try:
- # if gem_port.multicast:
- # self.log.debug('id-or-vid', id_or_vid=id_or_vid)
- # vid = self.olt.multicast_vlans[0] if len(self.olt.multicast_vlans) else None
- # if vid is not None:
- # self.add_mcast_gem_port(gem_port, vid)
- #
- # except Exception as e:
- # self.log.exception('id-or-vid', e=e)
-
- # TODO: Need to clean up TCont and GEM-Port on ONU delete in non-xPON mode
- _results = yield onu.create(tconts, gem_ports)
-
- except Exception as e:
- self.log.exception('add-onu', serial_number=serial_number_64, e=e)
- # allowable exception. H/w re-sync will recover any issues
+ except Exception as e:
+ self.log.warning('add-onu', serial_number=serial_number_64, e=e)
+ # allowable exception. H/w re-sync will recover/fix any issues
@property
def get_next_onu_id(self):
@@ -910,29 +858,12 @@
if onu is not None:
try:
- # Remove from xPON config (TODO: Deprecate this by refactoring ONU add steps)
- name = 'customer-{}-{}'.format(self.pon_id, onu_id)
- self._v_ont_anis.pop(name, None)
- self._ont_anis.pop(name, None)
-
- tcont_name = 'tcont-{}-{}-data'.format(self.pon_id, onu_id)
- self._tconts.pop(tcont_name, None)
-
- gem_ids = {gem_port.gem_id for gem_port in onu.gem_ports}
- for gem_id in gem_ids:
- gem_port_name = 'gem-{}-{}-{}'.format(self.pon_id, onu_id, gem_id)
- self._gem_ports.pop(gem_port_name, None)
-
- except Exception as e:
- self.log.exception('onu-delete-cleanup', serial_number=onu.serial_number, e=e)
-
- try:
- # Remove from hardware
- onu.delete()
+ proxy_address = onu.proxy_address
+ onu.delete() # Remove from hardware
# And removal from VOLTHA adapter agent
if not hw_only:
- self._parent.delete_child_device(onu.proxy_address)
+ self._parent.delete_child_device(proxy_address)
except Exception as e:
self.log.exception('onu-delete', serial_number=onu.serial_number, e=e)
@@ -961,122 +892,3 @@
assert len(self.olt.multicast_vlans) == 1, 'Only support 1 MCAST VLAN until BBF Support'
self._mcast_gem_ports[vlan] = mcast_gem
-
- # ===========================================================================
- #
- # Some xPON methods that need refactoring
-
- @property
- def tconts(self):
- return self._tconts
-
- @property
- def gem_ports(self):
- return self._gem_ports
-
- def get_device_profile_info(self, serial_number):
- vont_ani_info = next((info for _, info in self._v_ont_anis.items()
- if info.get('expected-serial-number') == serial_number), None)
- # New ONU?
- activate_onu = vont_ani_info is None
-
- tconts = list()
- gem_ports = list()
-
- if activate_onu:
- onu_id = self.get_next_onu_id
- name = 'customer-{}-{}'.format(self.pon_id, onu_id)
- vont_ani_info = {
- 'name' : name,
- 'enabled' : True,
- 'description' : '',
- 'pon-id' : self.pon_id,
- 'onu-id' : onu_id,
- 'expected-serial-number' : serial_number,
- 'expected-registration-id': '', # TODO: How about this?
- 'upstream-channel-speed' : 10000000000,
- }
- ont_ani_info = {
- 'name': name,
- 'description': '',
- 'enabled': True,
- 'upstream-fec': True,
- 'mgnt-gemport-aes': False
- }
- assert name not in self._v_ont_anis
- assert name not in self._ont_anis
- self._v_ont_anis[name] = vont_ani_info
- self._ont_anis[name] = ont_ani_info
-
- tcont, tc, td = self.create_xpon_tcont(onu_id)
- from xpon.olt_tcont import OltTCont
- tc['object'] = OltTCont.create(tc,
- OltTrafficDescriptor.create(tc['td-ref']),
- self.pon_id, onu_id)
- self._tconts[tcont.name] = tc['object']
- tconts.append(tc)
-
- # Now create the User-Data GEM-Ports
- num_priorities = 1 # TODO: Pull from tech-profile later
- for index in range(0, num_priorities):
- gem_port, gp = self.create_xpon_gem_port(onu_id, index, tcont)
-
- from xpon.olt_gem_port import OltGemPort
- gp['object'] = OltGemPort.create(self, gp, tcont.alloc_id, self.pon_id, onu_id)
- self._gem_ports[gem_port.name] = gp['object']
- gem_ports.append(gp)
-
- return activate_onu, tconts, gem_ports
-
- def create_xpon_gem_port(self, onu_id, index, tcont):
- # gem port creation (this initial one is for untagged ONU data support / EAPOL)
- gem_port = GemportsConfigData()
- gem_port.name = 'gem-{}-{}-{}'.format(self.pon_id, onu_id, gem_port.gemport_id)
- pon_intf_onu_id = (self.pon_id, onu_id)
- gem_port.gemport_id = self._parent.resource_mgr.get_gemport_id(pon_intf_onu_id)
- # TODO: Add release of alloc_id on ONU delete and/or TCONT delete
-
- gem_port.tcont_ref = tcont.name
- gp = {
- 'name': gem_port.name,
- 'gemport-id': gem_port.gemport_id,
- 'tcont-ref': gem_port.tcont_ref,
- 'encryption': False,
- 'traffic-class': 0,
- 'data': gem_port
- }
- return gem_port, gp
-
- def create_xpon_tcont(self, onu_id):
- """ Create the xPON TCONT Config data """
- tcont = TcontsConfigData()
- tcont.name = 'tcont-{}-{}-data'.format(self.pon_id, onu_id)
- pon_intf_onu_id = (self.pon_id, onu_id)
- tcont.alloc_id = self._parent.resource_mgr.get_alloc_id(pon_intf_onu_id)
- # TODO: Add release of alloc_id on ONU delete and/or TCONT delete
-
- traffic_desc = TrafficDescriptorProfileData(name='BestEffort',
- fixed_bandwidth=0,
- assured_bandwidth=0,
- maximum_bandwidth=10000000000,
- priority=0,
- weight=0,
- additional_bw_eligibility_indicator=0)
- tc = {
- 'name': tcont.name,
- 'alloc-id': tcont.alloc_id,
- 'pon-id': self.pon_id,
- 'onu-id': onu_id,
- 'td-ref': { # TODO: This should be the TD Name and the TD installed in xpon cache
- 'name': traffic_desc.name,
- 'fixed-bandwidth': traffic_desc.fixed_bandwidth,
- 'assured-bandwidth': traffic_desc.assured_bandwidth,
- 'maximum-bandwidth': traffic_desc.maximum_bandwidth,
- 'priority': traffic_desc.priority,
- 'weight': traffic_desc.weight,
- 'additional-bw-eligibility-indicator': 0,
- 'data': traffic_desc
- },
- 'data': tcont
- }
- return tcont, tc, traffic_desc
diff --git a/voltha/adapters/adtran_olt/resources/adtran_olt_resource_manager.py b/voltha/adapters/adtran_olt/resources/adtran_olt_resource_manager.py
index 76fbab2..c6ce41b 100644
--- a/voltha/adapters/adtran_olt/resources/adtran_olt_resource_manager.py
+++ b/voltha/adapters/adtran_olt/resources/adtran_olt_resource_manager.py
@@ -58,6 +58,10 @@
self.device_id, self.args.backend,
host, port
)
+ # Tech profiles uses this resource manager to retrieve information on a per-interface
+ # basis
+ self.resource_managers = {intf_id: self.resource_mgr for intf_id in device_info.intf_ids}
+
# Flag to indicate whether information fetched from device should
# be used to initialize PON Resource Ranges
self.use_device_info = False
@@ -71,7 +75,8 @@
def get_onu_id(self, pon_intf_id):
onu_id = self.resource_mgr.get_resource_id(pon_intf_id,
PONResourceManager.ONU_ID,
- 1)
+ onu_id=None,
+ num_of_id=1)
if onu_id is not None:
pon_intf_onu_id = (pon_intf_id, onu_id)
self.resource_mgr.init_resource_map(pon_intf_onu_id)
@@ -97,12 +102,10 @@
# ONU.
return alloc_id_list[0]
- alloc_id_list = self.resource_mgr.get_resource_id(
- pon_intf_id=pon_intf,
- onu_id=onu_id,
- resource_type=PONResourceManager.ALLOC_ID,
- num_of_id=1
- )
+ alloc_id_list = self.resource_mgr.get_resource_id(pon_intf,
+ PONResourceManager.ALLOC_ID,
+ onu_id=onu_id,
+ num_of_id=1)
if alloc_id_list and len(alloc_id_list) == 0:
self.log.error("no-alloc-id-available")
return None
@@ -118,43 +121,37 @@
return alloc_id
- def get_gemport_id(self, pon_intf_onu_id):
+ def get_gemport_id(self, pon_intf_onu_id, num_of_id=1):
+ # TODO: Remove this if never used
# Derive the pon_intf and onu_id from the pon_intf_onu_id tuple
pon_intf = pon_intf_onu_id[0]
onu_id = pon_intf_onu_id[1]
+ uni_id = pon_intf_onu_id[2]
+ assert False, 'unused function'
- gemport_id_list = self.resource_mgr.get_current_gemport_ids_for_onu(
+ gemport_id_list = self.resource_managers[pon_intf].get_current_gemport_ids_for_onu(
pon_intf_onu_id)
-
if gemport_id_list and len(gemport_id_list) > 0:
- # Since we support only one gemport_id for the ONU at the moment,
- # return the first gemport_id in the list, if available, for that
- # ONU.
- return gemport_id_list[0]
+ return gemport_id_list
- gemport_id_list = self.resource_mgr.get_resource_id(
+ gemport_id_list = self.resource_mgrs[pon_intf].get_resource_id(
pon_intf_id=pon_intf,
resource_type=PONResourceManager.GEMPORT_ID,
- num_of_id=1
+ num_of_id=num_of_id
)
- if gemport_id_list is None or len(gemport_id_list) == 0:
+
+ if gemport_id_list and len(gemport_id_list) == 0:
self.log.error("no-gemport-id-available")
return None
# update the resource map on KV store with the list of gemport_id
# allocated for the pon_intf_onu_id tuple
- self.resource_mgr.update_gemport_ids_for_onu(pon_intf_onu_id,
- gemport_id_list)
- # We currently use only one gemport
- gemport = gemport_id_list[0]
+ self.resource_managers[pon_intf].update_gemport_ids_for_onu(pon_intf_onu_id,
+ gemport_id_list)
- pon_intf_gemport = (pon_intf, gemport)
-
- # This information is used when packet_indication is received and
- # we need to derive the ONU Id for which the packet arrived based
- # on the pon_intf and gemport available in the packet_indication
- self.kv_store[str(pon_intf_gemport)] = str(onu_id)
- return gemport
+ self.update_gemports_ponport_to_onu_map_on_kv_store(gemport_id_list,
+ pon_intf, onu_id, uni_id)
+ return gemport_id_list
def free_pon_resources_for_onu(self, pon_intf_id_onu_id):
""" Typically called on ONU delete """
@@ -222,3 +219,69 @@
# After we have initialized resource ranges, initialize the
# resource pools accordingly.
self.resource_mgr.init_device_resource_pool()
+
+ def get_current_gemport_ids_for_onu(self, pon_intf_onu_id):
+ pon_intf_id = pon_intf_onu_id[0]
+ return self.resource_managers[pon_intf_id].get_current_gemport_ids_for_onu(pon_intf_onu_id)
+
+ def get_current_alloc_ids_for_onu(self, pon_intf_onu_id):
+ pon_intf_id = pon_intf_onu_id[0]
+ alloc_ids = self.resource_managers[pon_intf_id].get_current_alloc_ids_for_onu(pon_intf_onu_id)
+ if alloc_ids is None:
+ return None
+ # We support only one tcont at the moment
+ return alloc_ids[0]
+
+ def update_gemports_ponport_to_onu_map_on_kv_store(self, gemport_list, pon_port, onu_id, uni_id):
+ for gemport in gemport_list:
+ pon_intf_gemport = (pon_port, gemport)
+ # This information is used when packet_indication is received and
+ # we need to derive the ONU Id for which the packet arrived based
+ # on the pon_intf and gemport available in the packet_indication
+ self.kv_store[str(pon_intf_gemport)] = ' '.join(map(str, (onu_id, uni_id)))
+
+ def get_onu_uni_from_ponport_gemport(self, pon_port, gemport):
+ pon_intf_gemport = (pon_port, gemport)
+ return tuple(map(int, self.kv_store[str(pon_intf_gemport)].split(' ')))
+
+ def get_flow_id(self, pon_intf_id, onu_id, uni_id, flow_store_cookie, flow_category=None):
+ pon_intf_onu_id = (pon_intf_id, onu_id, uni_id)
+ try:
+ flow_ids = self.resource_managers[pon_intf_id]. \
+ get_current_flow_ids_for_onu(pon_intf_onu_id)
+ if flow_ids is not None:
+ for flow_id in flow_ids:
+ flows = self.get_flow_id_info(pon_intf_id, onu_id, uni_id, flow_id)
+ assert (isinstance(flows, list))
+ for flow in flows:
+
+ if flow_category is not None and \
+ 'flow_category' in flow and \
+ flow['flow_category'] == flow_category:
+ return flow_id
+ if flow['flow_store_cookie'] == flow_store_cookie:
+ return flow_id
+ except Exception as e:
+ self.log.error("error-retrieving-flow-info", e=e)
+
+ flow_id = self.resource_managers[pon_intf_id].get_resource_id(
+ pon_intf_onu_id[0], PONResourceManager.FLOW_ID)
+ if flow_id is not None:
+ self.resource_managers[pon_intf_id].update_flow_id_for_onu(
+ pon_intf_onu_id, flow_id
+ )
+
+ return flow_id
+
+ def get_flow_id_info(self, pon_intf_id, onu_id, uni_id, flow_id):
+ pon_intf_onu_id = (pon_intf_id, onu_id, uni_id)
+ return self.resource_managers[pon_intf_id].get_flow_id_info(pon_intf_onu_id, flow_id)
+
+ def get_current_flow_ids_for_uni(self, pon_intf_id, onu_id, uni_id):
+ pon_intf_onu_id = (pon_intf_id, onu_id, uni_id)
+ return self.resource_managers[pon_intf_id].get_current_flow_ids_for_onu(pon_intf_onu_id)
+
+ def update_flow_id_info_for_uni(self, pon_intf_id, onu_id, uni_id, flow_id, flow_data):
+ pon_intf_onu_id = (pon_intf_id, onu_id, uni_id)
+ return self.resource_managers[pon_intf_id].update_flow_id_info_for_onu(
+ pon_intf_onu_id, flow_id, flow_data)
\ No newline at end of file
diff --git a/voltha/adapters/adtran_olt/resources/adtran_resource_manager.py b/voltha/adapters/adtran_olt/resources/adtran_resource_manager.py
index 31fec4b..db56616 100644
--- a/voltha/adapters/adtran_olt/resources/adtran_resource_manager.py
+++ b/voltha/adapters/adtran_olt/resources/adtran_resource_manager.py
@@ -23,6 +23,7 @@
from bitstring import BitArray
import json
from common.pon_resource_manager.resource_manager import PONResourceManager
+from adtran_tech_profile import AdtnTechProfile
import adtranolt_platform as platform
@@ -153,30 +154,43 @@
:param pon_intf_id: OLT PON interface id
:param resource_type: String to identify type of resource
:param num_of_id: required number of ids
- :param onu_id: ONU ID if unique per ONU
+ :param onu_id: ONU ID if unique per ONU (Used for Alloc IDs)
:return list/int/None: list, int or None if resource type is
alloc_id/gemport_id, onu_id or invalid type
respectively
"""
result = None
+ if num_of_id < 1:
+ self._log.error("invalid-num-of-resources-requested")
+ return result
+
path = self._get_path(pon_intf_id, resource_type)
if path is None:
return result
try:
resource = self._get_resource(path, onu_id)
- if resource is None:
- raise Exception("get-resource-failed")
-
- if resource_type == PONResourceManager.ONU_ID:
+ if resource is not None and \
+ (resource_type == PONResourceManager.ONU_ID or
+ resource_type == PONResourceManager.FLOW_ID):
result = self._generate_next_id(resource)
- elif resource_type == PONResourceManager.GEMPORT_ID:
- result = [self._generate_next_id(resource) for _ in range(num_of_id)]
+ elif resource is not None and \
+ resource_type == PONResourceManager.GEMPORT_ID:
+ if num_of_id == 1:
+ result = self._generate_next_id(resource)
+ else:
+ result = [self._generate_next_id(resource) for _ in range(num_of_id)]
- elif resource_type == PONResourceManager.ALLOC_ID:
- result = [self._generate_next_id(resource, onu_id) for _ in range(num_of_id)]
+ elif resource is not None and \
+ resource_type == PONResourceManager.ALLOC_ID:
+ if num_of_id == 1:
+ result = self._generate_next_id(resource, onu_id)
+ else:
+ result = [self._generate_next_id(resource, onu_id) for _ in range(num_of_id)]
+ else:
+ raise Exception("get-resource-failed")
self._log.debug("Get-" + resource_type + "-success", result=result,
path=path)
@@ -208,7 +222,7 @@
try:
resource = self._get_resource(path, onu_id=onu_id)
if resource is None:
- raise Exception("get-resource-failed")
+ raise Exception("get-resource-for-free-failed")
if resource_type == PONResourceManager.ONU_ID:
self._release_id(resource, release_content)
@@ -221,7 +235,7 @@
for content in release_content:
self._release_id(resource, content, onu_id)
else:
- raise Exception("get-resource-failed")
+ raise Exception("get-resource-for-free-failed")
self._log.debug("Free-" + resource_type + "-success", path=path)
diff --git a/voltha/adapters/adtran_olt/resources/adtranolt_platform.py b/voltha/adapters/adtran_olt/resources/adtranolt_platform.py
index de0f317..7a6abe5 100644
--- a/voltha/adapters/adtran_olt/resources/adtranolt_platform.py
+++ b/voltha/adapters/adtran_olt/resources/adtranolt_platform.py
@@ -33,21 +33,6 @@
Encoding of identifiers
=======================
-GEM port ID
-
- GEM port id is unique per PON port and ranges
-
- 9 3 2 0
- +--------------+------+
- | onu id | GEM |
- | | idx |
- +--------------+------+
-
- GEM port id range (0..1023) is reserved, by standard
- Minimum GEM Port on Adtran OLT is 2176 (0x880)
- onu id = 7 bits = 128 ONUs per PON
- GEM index = 3 bits = 8 GEM ports per ONU
-
Alloc ID
Uniquely identifies a T-CONT
@@ -110,9 +95,6 @@
MIN_TCONT_ALLOC_ID = 1024 # 1024..16383
MAX_TCONT_ALLOC_ID = 16383
-# MIN_GEM_PORT_ID = 1023 # 1023..65534
-# MAX_GEM_PORT_ID = 65534
-
MIN_GEM_PORT_ID = 2176 # 2176..4222
MAX_GEM_PORT_ID = MIN_GEM_PORT_ID + 2046
@@ -125,18 +107,25 @@
def __init__(self):
pass
- def mk_uni_port_num(self, intf_id, onu_id):
- return intf_id << 11 | onu_id << 4
+ def mk_uni_port_num(self, intf_id, onu_id, uni_id=0):
+ return intf_id << 11 | onu_id << 4 | uni_id
+
+ def uni_id_from_uni_port(self, uni_port):
+ return uni_port & 0xF
-def mk_uni_port_num(intf_id, onu_id):
+def mk_uni_port_num(intf_id, onu_id, uni_id=0):
"""
Create a unique virtual UNI port number based up on PON and ONU ID
:param intf_id:
:param onu_id: (int) ONU ID (0..max)
:return: (int) UNI Port number
"""
- return intf_id << 11 | onu_id << 4
+ return intf_id << 11 | onu_id << 4 | uni_id
+
+
+def uni_id_from_uni_port(uni_port):
+ return uni_port & 0xF
def intf_id_from_uni_port_num(port_num):
@@ -163,33 +152,6 @@
return alloc_id
-# def mk_gemport_id(_, onu_id, idx=0): # Deprecated, moved to resource manager
-# """
-# Allocate a GEM-PORT ID. This is only called by the OLT
-#
-# A 4-bit mask was used since we need a gvid for untagged-EAPOL
-# traffic and then up to 8 more for user-user data priority
-# levels.
-#
-# :param _: (int) PON ID (0..n) - not used
-# :param onu_id: (int) ONU ID (0..MAX_ONUS_PER_PON-1)
-# :param idx: (int) GEM_PORT Index (0..15)
-# """
-# return MIN_GEM_PORT_ID + (onu_id << 4) + idx
-
-
-def intf_id_to_port_no(intf_id, intf_type):
- if intf_type is Port.ETHERNET_NNI:
- # OpenOLT starts at 128. We start at 1 (one-to-one mapping)
- return intf_id
- elif intf_type is Port.PON_OLT:
- # OpenOLT sets bit 29 + intf_id. We start at 5 for now for PON 0
- # return 0x2 << 28 | intf_id
- return intf_id + 5 # see _pon_id_to_port_number
- else:
- raise Exception('Invalid port type')
-
-
def intf_id_from_nni_port_num(port_num):
# OpenOLT starts at 128. We start at 1 (one-to-one mapping)
# return port_num - 128
diff --git a/voltha/adapters/adtran_olt/xpon/adtran_xpon.py b/voltha/adapters/adtran_olt/xpon/adtran_xpon.py
deleted file mode 100644
index 30e5919..0000000
--- a/voltha/adapters/adtran_olt/xpon/adtran_xpon.py
+++ /dev/null
@@ -1,292 +0,0 @@
-# Copyright 2017-present Adtran, Inc.
-#
-# 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 traffic_descriptor import TrafficDescriptor
-from voltha.protos.bbf_fiber_tcont_body_pb2 import TcontsConfigData
-from voltha.protos.bbf_fiber_traffic_descriptor_profile_body_pb2 import TrafficDescriptorProfileData
-from voltha.protos.bbf_fiber_gemport_body_pb2 import GemportsConfigData
-
-log = structlog.get_logger()
-
-
-class AdtranXPON(object):
- """
- Class to abstract common OLT and ONU xPON operations
- """
- def __init__(self, **kwargs):
- # xPON config dictionaries
- self._v_ont_anis = {} # Name -> dict
- self._ont_anis = {} # Name -> dict
- self._tconts = {} # Name -> dict
- self._traffic_descriptors = {} # Name -> dict
- self._gem_ports = {} # Name -> dict
-
- @property
- def tconts(self):
- return self._tconts
-
- @property
- def traffic_descriptors(self):
- return self._traffic_descriptors
-
- @property
- def gem_ports(self):
- return self._gem_ports
-
- def _get_xpon_collection(self, data):
- """
- Get the collection for the object type and handler routines
- :param data: xPON object TODO: These three are still needed for the ONU until
- xPON is deprecated as the OLT calls into the ONU
- to start it up and passes these three ProtoBuf
- messages.
- """
- if isinstance(data, TcontsConfigData):
- return self.tconts, \
- self.on_tcont_create,\
- self.on_tcont_modify, \
- self.on_tcont_delete
-
- elif isinstance(data, TrafficDescriptorProfileData):
- return self.traffic_descriptors, \
- self.on_td_create,\
- self.on_td_modify, \
- self.on_td_delete
-
- elif isinstance(data, GemportsConfigData):
- return self.gem_ports, \
- self.on_gemport_create,\
- self.on_gemport_modify, \
- self.on_gemport_delete
-
- return None, None, None, None
-
- def _data_to_dict(self, data, td=None):
- if isinstance(data, TcontsConfigData):
- return 'TCONT', {
- 'name': data.name,
- 'alloc-id': data.alloc_id,
- 'vont-ani': data.interface_reference,
- 'td-ref': td['name'],
- 'data': data
- }
- elif isinstance(data, TrafficDescriptorProfileData):
- additional = TrafficDescriptor.AdditionalBwEligibility.from_value(
- data.additional_bw_eligibility_indicator)
-
- return 'Traffic-Desc', {
- 'name': data.name,
- 'fixed-bandwidth': data.fixed_bandwidth,
- 'assured-bandwidth': data.assured_bandwidth,
- 'maximum-bandwidth': data.maximum_bandwidth,
- 'priority': data.priority,
- 'weight': data.weight,
- 'additional-bw-eligibility-indicator': additional,
- 'data': data
- }
- elif isinstance(data, GemportsConfigData):
- return 'GEMPort', {
- 'name': data.name,
- 'gemport-id': data.gemport_id,
- 'tcont-ref': data.tcont_ref,
- 'encryption': data.aes_indicator,
- 'traffic-class': data.traffic_class,
- 'venet-ref': data.itf_ref, # vENET
- 'data': data
- }
- return None
-
- def create_tcont(self, tcont_data, td_data):
- """
- Create TCONT information
- :param tcont_data:
- :param td_data:
- """
- log.debug('create-tcont', tcont=tcont_data, td=td_data)
-
- # Handle TD first, then TCONT
- if td_data is not None:
- try:
- self.xpon_create(td_data)
-
- except Exception as e:
- log.exception('td-create', td=td_data)
-
- try:
- td = self.traffic_descriptors.get(td_data.name) if td_data is not None else None
- self.xpon_create(tcont_data, td=td)
-
- except Exception as e:
- log.exception('tcont-create', tcont=tcont_data)
-
- def update_tcont(self, tcont_data, traffic_descriptor_data):
- """
- Update TCONT information
- :param tcont_data:
- :param traffic_descriptor_data:
- """
- log.debug('update-tcont', tcont=tcont_data, td=traffic_descriptor_data)
-
- # Handle TD first, then TCONT. The TD may be new
- try:
- items, _, _, _ = self._get_xpon_collection(traffic_descriptor_data)
- existing_item = items.get(traffic_descriptor_data.name)
- if existing_item is None:
- self.xpon_create(traffic_descriptor_data)
- else:
- self.xpon_update(traffic_descriptor_data)
-
- except Exception as e:
- log.exception('td-update', td=traffic_descriptor_data)
-
- try:
- self.xpon_update(tcont_data)
-
- except Exception as e:
- log.exception('tcont-update', tcont=tcont_data)
-
- def remove_tcont(self, tcont_data, traffic_descriptor_data):
- """
- Remove TCONT information
- :param tcont_data:
- :param traffic_descriptor_data:
- """
- log.debug('remove-tcont', tcont=tcont_data, td=traffic_descriptor_data)
-
- # Handle TCONT first when removing, then TD
- try:
- self.xpon_remove(traffic_descriptor_data)
- except Exception as e:
- log.exception('td-update', td=traffic_descriptor_data)
-
- try:
- self.xpon_remove(tcont_data)
- except Exception as e:
- log.exception('tcont-update', tcont=tcont_data)
-
- def xpon_create(self, data, td=None):
- log.debug('xpon-create', data=data)
- name = data.name
- items, create_method, update_method, _ = self._get_xpon_collection(data)
-
- if items is None:
- from voltha.adapters.adtran_olt.adtran_olt_handler import OnuIndication
- if isinstance(data, OnuIndication): # Ignore this
- return
- raise ValueError('Unknown data type: {}'.format(type(data)))
-
- item_type, new_item = self._data_to_dict(data, td=td)
-
- if name in items:
- # Treat like an update. It will update collection if needed
- return self.xpon_update(data, td=td)
-
- log.debug('new-item', item_type=item_type, item=new_item)
- items[name] = new_item
-
- if create_method is not None:
- try:
- new_item = create_method(new_item)
- except Exception as e:
- log.exception('xpon-create', item=new_item, e=e)
-
- if new_item is not None:
- items[name] = new_item
- else:
- del items[name]
-
- def xpon_update(self, data, td=None):
- log.debug('xpon-update', data=data)
- name = data.name
- items, create, update_method, delete = self._get_xpon_collection(data)
-
- if items is None:
- raise ValueError('Unknown data type: {}'.format(type(data)))
-
- existing_item = items.get(name)
- if existing_item is None:
- raise KeyError("'{}' not found. Type: {}".format(name, type(data)))
-
- item_type, update_item = self._data_to_dict(data, td=td)
- log.debug('update-item', item_type=item_type, item=update_item)
-
- def _dict_diff(lhs, rhs):
- """
- Compare the values of two dictionaries and return the items in 'rhs'
- that are different than 'lhs. The RHS dictionary keys can be a subset of the
- LHS dictionary, or the RHS dictionary keys can contain new values.
-
- :param lhs: (dict) Original dictionary values
- :param rhs: (dict) New dictionary values to compare to the original (lhs) dict
- :return: (dict) Dictionary with differences from the RHS dictionary
- """
- lhs_keys = {k for k in lhs.keys() if k not in ['object', 'data']}
- rhs_keys = {k for k in rhs.keys() if k not in ['object', 'data']}
- assert len(lhs_keys) == len(lhs_keys & rhs_keys), 'Dictionary Keys do not match'
- return {k: v for k, v in rhs.items() if k not in lhs or lhs[k] != rhs[k]}
-
- # Calculate the difference
- diffs = _dict_diff(existing_item, update_item)
-
- if len(diffs) == 0:
- log.debug('update-item-no-diffs')
- return
-
- items[name] = update_item
-
- # Act on any changed items
- if update_method is not None:
- try:
- update_item = update_method(existing_item, update_item, diffs)
- except Exception as e:
- log.exception('xpon-update', existing=existing_item,
- update=update_item, diffs=diffs,
- e=e)
-
- if update_item is not None:
- items[name] = update_item
- else:
- del items[name]
-
- def xpon_remove(self, data):
- log.debug('xpon_remove', data=data)
- raise NotImplementedError("xPON support has been disabled")
-
- def on_tcont_create(self, tcont):
- return tcont # Implement in your OLT, if needed
-
- def on_tcont_modify(self, tcont, update, diffs):
- return update # Implement in your OLT, if needed
-
- def on_tcont_delete(self, tcont):
- return None # Implement in your OLT, if needed
-
- def on_td_create(self, traffic_desc):
- return traffic_desc # Implement in your OLT, if needed
-
- def on_td_modify(self, traffic_desc, update, diffs):
- return update # Implement in your OLT, if needed
-
- def on_td_delete(self, traffic_desc):
- return None # Implement in your OLT, if needed
-
- def on_gemport_create(self, gem_port):
- return gem_port # Implement in your OLT, if needed
-
- def on_gemport_modify(self, gem_port, update, diffs):
- return update # Implement in your OLT, if needed
-
- def on_gemport_delete(self, gem_port):
- return None # Implement in your OLT, if needed
diff --git a/voltha/adapters/adtran_olt/xpon/gem_port.py b/voltha/adapters/adtran_olt/xpon/gem_port.py
index 50149b5..d14b4f2 100644
--- a/voltha/adapters/adtran_olt/xpon/gem_port.py
+++ b/voltha/adapters/adtran_olt/xpon/gem_port.py
@@ -17,23 +17,25 @@
"""
Class to wrap TCont capabilities
"""
- def __init__(self, gem_id, alloc_id,
+ def __init__(self, gem_id, alloc_id, tech_profile_id,
encryption=False,
omci_transport=False,
multicast=False,
tcont_ref=None,
traffic_class=None,
- name=None,
- handler=None):
- self.name = name
+ handler=None,
+ is_mock=False):
+
self.gem_id = gem_id
self._alloc_id = alloc_id
+ self.tech_profile_id = tech_profile_id
self.tcont_ref = tcont_ref
self.traffic_class = traffic_class
self._encryption = encryption
self._omci_transport = omci_transport
self.multicast = multicast
self._handler = handler
+ self._is_mock = is_mock
# TODO: Make this a base class and derive OLT and ONU specific classes from it
# The primary thing to change is the PON ID is OLT specific and the add/remove
@@ -42,6 +44,8 @@
self._onu_id = None
self._intf_id = None
+ self.tech_profile_id = None # TODO: Make property and clean up object once tech profiles fully supported
+
# Statistics
self.rx_packets = 0
self.rx_bytes = 0
@@ -49,9 +53,7 @@
self.tx_bytes = 0
def __str__(self):
- return "GemPort: {}, alloc-id: {}, gem-id: {}".format(self.name,
- self.alloc_id,
- self.gem_id)
+ return "GemPort: alloc-id: {}, gem-id: {}".format(self.alloc_id,self.gem_id)
@property
def pon_id(self):
@@ -97,6 +99,10 @@
return tcont_item.get('object') if tcont_item is not None else None
@property
+ def encryption(self):
+ return self._encryption
+
+ @property
def omci_transport(self):
return self._omci_transport
diff --git a/voltha/adapters/adtran_olt/xpon/olt_gem_port.py b/voltha/adapters/adtran_olt/xpon/olt_gem_port.py
index d4abe27..3d2210d 100644
--- a/voltha/adapters/adtran_olt/xpon/olt_gem_port.py
+++ b/voltha/adapters/adtran_olt/xpon/olt_gem_port.py
@@ -1,4 +1,4 @@
-
+#
# Copyright 2017-present Adtran, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,6 +18,7 @@
from gem_port import GemPort
from twisted.internet.defer import inlineCallbacks, returnValue
+from ..adtran_olt_handler import AdtranOltHandler
log = structlog.get_logger()
@@ -26,44 +27,39 @@
"""
Adtran OLT specific implementation
"""
- def __init__(self, gem_id, alloc_id, pon_id, onu_id,
+ def __init__(self, gem_id, alloc_id, tech_profile_id, pon_id, onu_id,
encryption=False,
omci_transport=False,
multicast=False,
tcont_ref=None,
traffic_class=None,
- name=None,
handler=None,
- is_mock=False,
- pb_data=None):
- super(OltGemPort, self).__init__(gem_id, alloc_id,
+ is_mock=False):
+ super(OltGemPort, self).__init__(gem_id, alloc_id, tech_profile_id,
encryption=encryption,
omci_transport=omci_transport,
multicast=multicast,
tcont_ref=tcont_ref,
traffic_class=traffic_class,
- name=name,
- handler=handler)
- self._is_mock = is_mock
+ handler=handler,
+ is_mock=is_mock)
self._timestamp = None
self.pon_id = pon_id
self.onu_id = onu_id
- self.data = pb_data # Needed for non-xPON mode
+
+ def __str__(self):
+ return "GemPort: {}/{}, alloc-id: {}, gem-id: {}".format(self.pon_id, self.onu_id,
+ self.alloc_id, self.gem_id)
@staticmethod
- def create(handler, gem_port, alloc_id, pon_id, onu_id):
- mcast = False # gem_port['gemport-id'] in [4095] # TODO: Perform proper lookup
-
- return OltGemPort(gem_port['gemport-id'],
+ def create(handler, gem, alloc_id, tech_profile_id, pon_id, onu_id, _uni_id, _ofp_port_no):
+ return OltGemPort(gem.gemport_id,
alloc_id,
+ tech_profile_id,
pon_id, onu_id,
- encryption=gem_port['encryption'], # aes_indicator,
- tcont_ref=gem_port['tcont-ref'],
- name=gem_port['name'],
- traffic_class=gem_port['traffic-class'],
+ encryption=gem.aes_encryption.lower() == 'true',
handler=handler,
- multicast=mcast,
- pb_data=gem_port['data'])
+ multicast=False)
@property
def timestamp(self):
@@ -87,7 +83,8 @@
@inlineCallbacks
def add_to_hardware(self, session, operation='POST'):
- from ..adtran_olt_handler import AdtranOltHandler
+ if self._is_mock:
+ returnValue('mock')
uri = AdtranOltHandler.GPON_GEM_CONFIG_LIST_URI.format(self.pon_id, self.onu_id)
data = json.dumps(self.to_dict())
@@ -106,7 +103,8 @@
raise
def remove_from_hardware(self, session):
- from ..adtran_olt_handler import AdtranOltHandler
+ if self._is_mock:
+ returnValue('mock')
uri = AdtranOltHandler.GPON_GEM_CONFIG_URI.format(self.pon_id, self.onu_id, self.gem_id)
name = 'gem-port-delete-{}-{}: {}'.format(self.pon_id, self.onu_id, self.gem_id)
diff --git a/voltha/adapters/adtran_olt/xpon/olt_tcont.py b/voltha/adapters/adtran_olt/xpon/olt_tcont.py
index 804c8c2..d678bc3 100644
--- a/voltha/adapters/adtran_olt/xpon/olt_tcont.py
+++ b/voltha/adapters/adtran_olt/xpon/olt_tcont.py
@@ -14,8 +14,11 @@
import structlog
import json
-from twisted.internet.defer import inlineCallbacks, returnValue
+from twisted.internet.defer import inlineCallbacks, returnValue
from tcont import TCont
+from voltha.adapters.openolt.protos import openolt_pb2
+from olt_traffic_descriptor import OltTrafficDescriptor
+from ..adtran_olt_handler import AdtranOltHandler
log = structlog.get_logger()
@@ -24,29 +27,25 @@
"""
Adtran OLT specific implementation
"""
- def __init__(self, alloc_id, traffic_descriptor, pon_id, onu_id,
- name=None, is_mock=False,
- pb_data=None):
- super(OltTCont, self).__init__(alloc_id, traffic_descriptor, name=name)
- self._is_mock = is_mock
+ def __init__(self, alloc_id, tech_profile_id, traffic_descriptor, pon_id, onu_id, is_mock=False):
+ super(OltTCont, self).__init__(alloc_id, tech_profile_id, traffic_descriptor, is_mock=is_mock)
self.pon_id = pon_id
self.onu_id = onu_id
- self.data = pb_data # Needed for non-xPON mode
+
+ def __str__(self):
+ return "TCont: {}/{}, alloc-id: {}".format(self.pon_id, self.onu_id, self.alloc_id)
@staticmethod
- def create(tcont, td, pon_id, onu_id):
- from olt_traffic_descriptor import OltTrafficDescriptor
+ def create(tcont, pon_id, onu_id, tech_profile_id, uni_id, ofp_port_no):
+ # Only valid information in the upstream tcont of a tech profile
+ if tcont.direction != openolt_pb2.UPSTREAM:
+ return None
- assert isinstance(tcont, dict), 'TCONT should be a dictionary'
- assert isinstance(td, OltTrafficDescriptor), 'Invalid Traffic Descriptor data type'
-
- return OltTCont(tcont['alloc-id'], td, pon_id, onu_id,
- name=tcont['name'],
- pb_data=tcont['data'])
+ td = OltTrafficDescriptor.create(tcont, pon_id, onu_id, uni_id, ofp_port_no)
+ return OltTCont(tcont.alloc_id, tech_profile_id, td, pon_id, onu_id)
@inlineCallbacks
def add_to_hardware(self, session):
- from ..adtran_olt_handler import AdtranOltHandler
if self._is_mock:
returnValue('mock')
@@ -63,9 +62,8 @@
if self.traffic_descriptor is not None:
try:
- results = yield self.traffic_descriptor.add_to_hardware(session,
- self.pon_id, self.onu_id,
- self.alloc_id)
+ results = yield self.traffic_descriptor.add_to_hardware(session)
+
except Exception as e:
log.exception('traffic-descriptor', tcont=self,
td=self.traffic_descriptor, e=e)
@@ -74,12 +72,10 @@
returnValue(results)
def remove_from_hardware(self, session):
- from ..adtran_olt_handler import AdtranOltHandler
+ if self._is_mock:
+ returnValue('mock')
- pon_id = self.pon_id
- onu_id = self.onu_id # TODO: Cleanup parameters
-
- uri = AdtranOltHandler.GPON_TCONT_CONFIG_URI.format(pon_id, onu_id, self.alloc_id)
+ uri = AdtranOltHandler.GPON_TCONT_CONFIG_URI.format(self.pon_id, self.onu_id, self.alloc_id)
name = 'tcont-delete-{}-{}: {}'.format(self.pon_id, self.onu_id, self.alloc_id)
return session.request('DELETE', uri, name=name)
diff --git a/voltha/adapters/adtran_olt/xpon/olt_traffic_descriptor.py b/voltha/adapters/adtran_olt/xpon/olt_traffic_descriptor.py
index 635e5e1..f3c0713 100644
--- a/voltha/adapters/adtran_olt/xpon/olt_traffic_descriptor.py
+++ b/voltha/adapters/adtran_olt/xpon/olt_traffic_descriptor.py
@@ -16,6 +16,8 @@
import json
from traffic_descriptor import TrafficDescriptor
from twisted.internet.defer import inlineCallbacks, returnValue
+from ..adtran_olt_handler import AdtranOltHandler
+from voltha.adapters.openolt.protos import openolt_pb2
log = structlog.get_logger()
@@ -24,53 +26,56 @@
"""
Adtran ONU specific implementation
"""
- def __init__(self, fixed, assured, maximum,
+ def __init__(self, pon_id, onu_id, alloc_id, fixed, assured, maximum,
additional=TrafficDescriptor.AdditionalBwEligibility.DEFAULT,
best_effort=None,
- name=None,
- is_mock=False,
- pb_data=None):
+ is_mock=False):
super(OltTrafficDescriptor, self).__init__(fixed, assured, maximum,
additional=additional,
- best_effort=best_effort,
- name=name)
+ best_effort=best_effort)
+ self.pon_id = pon_id
+ self.onu_id = onu_id
+ self.alloc_id = alloc_id
self._is_mock = is_mock
- self.data = pb_data
@staticmethod
- def create(traffic_disc):
- from best_effort import BestEffort
+ def create(tcont, pon_id, onu_id, _uni_id, _ofp_port_no):
+ alloc_id = tcont.alloc_id
+ shaping_info = tcont.traffic_shaping_info
+ fixed = shaping_info.cir
+ assured = 0
+ maximum = shaping_info.pir
- assert isinstance(traffic_disc, dict), 'Traffic Descriptor should be a dictionary'
+ best_effort = None
+ # if shaping_info.add_bw_ind == openolt_pb2.InferredAdditionBWIndication_Assured:
+ # pass
+ # TODO: Support additional BW decode
+ # elif shaping_info.add_bw_ind == openolt_pb2.InferredAdditionBWIndication_BestEffort:
+ # pass
+ # additional = TrafficDescriptor.AdditionalBwEligibility.from_value(
+ # traffic_disc['additional-bw-eligibility-indicator'])
+ #
+ # if additional == TrafficDescriptor.AdditionalBwEligibility.BEST_EFFORT_SHARING:
+ # best_effort = BestEffort(traffic_disc['maximum-bandwidth'],
+ # traffic_disc['priority'],
+ # traffic_disc['weight'])
+ # else:
+ # best_effort = None
- additional = TrafficDescriptor.AdditionalBwEligibility.from_value(
- traffic_disc['additional-bw-eligibility-indicator'])
-
- if additional == TrafficDescriptor.AdditionalBwEligibility.BEST_EFFORT_SHARING:
- best_effort = BestEffort(traffic_disc['maximum-bandwidth'],
- traffic_disc['priority'],
- traffic_disc['weight'])
- else:
- best_effort = None
-
- return OltTrafficDescriptor(traffic_disc['fixed-bandwidth'],
- traffic_disc['assured-bandwidth'],
- traffic_disc['maximum-bandwidth'],
- name=traffic_disc['name'],
- best_effort=best_effort,
- additional=additional,
- pb_data=traffic_disc['data'])
+ return OltTrafficDescriptor(pon_id, onu_id, alloc_id,
+ fixed, assured, maximum, best_effort=best_effort)
@inlineCallbacks
- def add_to_hardware(self, session, pon_id, onu_id, alloc_id):
- from ..adtran_olt_handler import AdtranOltHandler
-
+ def add_to_hardware(self, session):
+ # TODO: Traffic descriptors are no longer shared, save pon and onu ID to base class
if self._is_mock:
returnValue('mock')
- uri = AdtranOltHandler.GPON_TCONT_CONFIG_URI.format(pon_id, onu_id, alloc_id)
+ uri = AdtranOltHandler.GPON_TCONT_CONFIG_URI.format(self.pon_id,
+ self.onu_id,
+ self.alloc_id)
data = json.dumps({'traffic-descriptor': self.to_dict()})
- name = 'tcont-td-{}-{}: {}'.format(pon_id, onu_id, alloc_id)
+ name = 'tcont-td-{}-{}: {}'.format(self.pon_id, self.onu_id, self.alloc_id)
try:
results = yield session.request('PATCH', uri, data=data, name=name)
@@ -78,16 +83,17 @@
log.exception('traffic-descriptor', td=self, e=e)
raise
- if self.additional_bandwidth_eligibility == \
- TrafficDescriptor.AdditionalBwEligibility.BEST_EFFORT_SHARING:
- if self.best_effort is None:
- raise ValueError('TCONT is best-effort but does not define best effort sharing')
-
- try:
- results = yield self.best_effort.add_to_hardware(session, pon_id, onu_id, alloc_id)
-
- except Exception as e:
- log.exception('best-effort', best_effort=self.best_effort, e=e)
- raise
+ # TODO: Add support for best-effort sharing
+ # if self.additional_bandwidth_eligibility == \
+ # TrafficDescriptor.AdditionalBwEligibility.BEST_EFFORT_SHARING:
+ # if self.best_effort is None:
+ # raise ValueError('TCONT is best-effort but does not define best effort sharing')
+ #
+ # try:
+ # results = yield self.best_effort.add_to_hardware(session)
+ #
+ # except Exception as e:
+ # log.exception('best-effort', best_effort=self.best_effort, e=e)
+ # raise
returnValue(results)
diff --git a/voltha/adapters/adtran_olt/xpon/tcont.py b/voltha/adapters/adtran_olt/xpon/tcont.py
index fd65adf..b5e8eee 100644
--- a/voltha/adapters/adtran_olt/xpon/tcont.py
+++ b/voltha/adapters/adtran_olt/xpon/tcont.py
@@ -17,13 +17,14 @@
"""
Class to wrap TCont capabilities
"""
- def __init__(self, alloc_id, traffic_descriptor, name=None):
+ def __init__(self, alloc_id, tech_profile_id, traffic_descriptor, is_mock=False):
self.alloc_id = alloc_id
self.traffic_descriptor = traffic_descriptor
- self.name = name
+ self._is_mock = is_mock
+ self.tech_profile_id = tech_profile_id
# TODO: Make this a base class and derive OLT and ONU specific classes from it
# The primary thing difference is the add/remove from hardware methods
def __str__(self):
- return "TCont: {}, alloc-id: {}".format(self.name, self.alloc_id)
+ return "TCont: alloc-id: {}".format(self.alloc_id)
diff --git a/voltha/adapters/adtran_olt/xpon/traffic_descriptor.py b/voltha/adapters/adtran_olt/xpon/traffic_descriptor.py
index 96b7ce5..230605b 100644
--- a/voltha/adapters/adtran_olt/xpon/traffic_descriptor.py
+++ b/voltha/adapters/adtran_olt/xpon/traffic_descriptor.py
@@ -48,9 +48,7 @@
def __init__(self, fixed, assured, maximum,
additional=AdditionalBwEligibility.DEFAULT,
- best_effort=None,
- name=None):
- self.name = name
+ best_effort=None):
self.fixed_bandwidth = fixed # bps
self.assured_bandwidth = assured # bps
self.maximum_bandwidth = maximum # bps
@@ -60,10 +58,9 @@
else None
def __str__(self):
- return "TrafficDescriptor: {}, {}/{}/{}".format(self.name,
- self.fixed_bandwidth,
- self.assured_bandwidth,
- self.maximum_bandwidth)
+ return "TrafficDescriptor: {}/{}/{}".format(self.fixed_bandwidth,
+ self.assured_bandwidth,
+ self.maximum_bandwidth)
def to_dict(self):
val = {
diff --git a/voltha/adapters/adtran_onu/adtran_onu.py b/voltha/adapters/adtran_onu/adtran_onu.py
index ecec76f..d8ae1b9 100755
--- a/voltha/adapters/adtran_onu/adtran_onu.py
+++ b/voltha/adapters/adtran_onu/adtran_onu.py
@@ -42,7 +42,7 @@
device_handler_class=AdtranOnuHandler,
name='adtran_onu',
vendor='ADTRAN, Inc.',
- version='1.23',
+ version='1.24',
device_type='adtran_onu',
vendor_id='ADTN',
accepts_add_remove_flow_updates=False), # TODO: Support flow-mods
@@ -117,16 +117,14 @@
self.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 is not None:
- handler = self.devices_handlers.get(device.id)
- if handler is not None:
- handler.rx_inter_adapter_message(msg)
+ handler = self.devices_handlers[device.id]
+ handler.event_messages.put(msg)
+ else:
+ self.log.error("device-not-found")
def abandon_device(self, device):
raise NotImplementedError('TODO: Not currently supported')
diff --git a/voltha/adapters/adtran_onu/adtran_onu_handler.py b/voltha/adapters/adtran_onu/adtran_onu_handler.py
index 040a617..f9ce52f 100644
--- a/voltha/adapters/adtran_onu/adtran_onu_handler.py
+++ b/voltha/adapters/adtran_onu/adtran_onu_handler.py
@@ -13,11 +13,14 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
-from voltha.adapters.adtran_olt.xpon.adtran_xpon import AdtranXPON
+import ast
from pon_port import PonPort
from uni_port import UniPort
from heartbeat import HeartBeat
from omci.omci import OMCI
+from onu_traffic_descriptor import OnuTrafficDescriptor
+from onu_tcont import OnuTCont
+from onu_gem_port import OnuGemPort
from voltha.extensions.alarms.adapter_alarms import AdapterAlarms
from voltha.extensions.kpi.onu.onu_pm_metrics import OnuPmMetrics
@@ -32,19 +35,25 @@
from voltha.protos.common_pb2 import OperStatus, ConnectStatus
from common.utils.indexpool import IndexPool
from voltha.extensions.omci.omci_me import *
+from common.tech_profile.tech_profile import TechProfile
+from voltha.core.config.config_backend import ConsulStore
+from voltha.core.config.config_backend import EtcdStore
import voltha.adapters.adtran_olt.resources.adtranolt_platform as platform
from voltha.adapters.adtran_onu.flow.flow_entry import FlowEntry
from omci.adtn_install_flow import AdtnInstallFlowTask
from omci.adtn_remove_flow import AdtnRemoveFlowTask
+from omci.adtn_tp_service_specific_task import AdtnTpServiceSpecificTask
+from common.tech_profile.tech_profile import DEFAULT_TECH_PROFILE_TABLE_ID
_ = third_party
_MAXIMUM_PORT = 17 # Only one PON and UNI port at this time
_ONU_REBOOT_MIN = 90 # IBONT 602 takes about 3 minutes
_ONU_REBOOT_RETRY = 10
+_STARTUP_RETRY_WAIT = 20
-class AdtranOnuHandler(AdtranXPON):
+class AdtranOnuHandler(object):
def __init__(self, adapter, device_id):
kwargs = dict()
super(AdtranOnuHandler, self).__init__(**kwargs)
@@ -58,14 +67,11 @@
self.pm_metrics = None
self.alarms = None
self._mgmt_gemport_aes = False
- self._upstream_channel_speed = 0
self._openomci = OMCI(self, adapter.omci_agent)
self._in_sync_subscription = None
- self._onu_port_number = 0
self._pon_port_number = 1
- self._port_number_pool = IndexPool(_MAXIMUM_PORT, 0)
self._unis = dict() # Port # -> UniPort
self._pon = PonPort.create(self, self._pon_port_number)
@@ -81,6 +87,30 @@
self.mac_bridge_service_profile_entity_id = self.vlan_tcis_1
self.gal_enet_profile_entity_id = 0 # Was 0x100, but ONU seems to overwrite and use zero
+ # Technology profile related values
+ self.incoming_messages = DeferredQueue()
+ self.event_messages = DeferredQueue()
+ self._tp_service_specific_task = dict()
+ self._tech_profile_download_done = dict()
+ self._upstream_channel_speed = 0 # TODO: Deprecate
+
+ # Initialize KV store client
+ self.args = registry('main').get_args()
+ if self.args.backend == 'etcd':
+ host, port = self.args.etcd.split(':', 1)
+ self.kv_client = EtcdStore(host, port,
+ TechProfile.KV_STORE_TECH_PROFILE_PATH_PREFIX)
+ elif self.args.backend == 'consul':
+ host, port = self.args.consul.split(':', 1)
+ self.kv_client = ConsulStore(host, port,
+ TechProfile.KV_STORE_TECH_PROFILE_PATH_PREFIX)
+ else:
+ self.log.error('Invalid-backend')
+ raise Exception("Invalid-backend-for-kv-store")
+
+ # Handle received ONU event messages
+ reactor.callLater(0, self.handle_onu_events)
+
def __str__(self):
return "AdtranOnuHandler: {}".format(self.device_id)
@@ -153,19 +183,12 @@
def pon_ports(self):
return [self._pon]
- @property
- def _next_port_number(self):
- return self._port_number_pool.get_next()
-
- def _release_port_number(self, number):
- self._port_number_pool.release(number)
-
def start(self):
assert self._enabled, 'Start should only be called if enabled'
self._cancel_deferred()
# Register for adapter messages
- #self.adapter_agent.register_for_inter_adapter_messages()
+ self.adapter_agent.register_for_inter_adapter_messages()
# OpenOMCI Startup
self._subscribe_to_events()
@@ -183,11 +206,10 @@
def stop(self):
assert not self._enabled, 'Stop should only be called if disabled'
-
self._cancel_deferred()
# Drop registration for adapter messages
- #self.adapter_agent.unregister_for_inter_adapter_messages()
+ self.adapter_agent.unregister_for_inter_adapter_messages()
# Heartbeat
self._heartbeat.enabled = False
@@ -221,7 +243,7 @@
self.adapter_agent.register_for_proxied_messages(device.proxy_address)
# initialize device info
- device.root = True
+ device.root = False
device.vendor = 'Adtran Inc.'
device.model = 'n/a'
device.hardware_version = 'n/a'
@@ -296,7 +318,7 @@
self.adapter_agent.register_for_proxied_messages(device.proxy_address)
# Register for adapter messages
- #self.adapter_agent.register_for_inter_adapter_messages()
+ self.adapter_agent.register_for_inter_adapter_messages()
# Set the connection status to REACHABLE
device.connect_status = ConnectStatus.REACHABLE
@@ -314,6 +336,179 @@
self.log.info('reconciling-ONU-device-ends')
+ @inlineCallbacks
+ def handle_onu_events(self):
+ # TODO: Add 'shutdown' message to exit loop
+ event_msg = yield self.event_messages.get()
+ try:
+ if event_msg['event'] == 'download_tech_profile':
+ tp_path = event_msg['event_data']
+ uni_id = event_msg['uni_id']
+ self.load_and_configure_tech_profile(uni_id, tp_path)
+
+ except Exception as e:
+ self.log.error("exception-handling-onu-event", e=e)
+
+ # Handle next event
+ reactor.callLater(0, self.handle_onu_events)
+
+ def _tp_path_to_tp_id(self, tp_path):
+ parts = tp_path.split('/')
+ if len(parts) > 2:
+ try:
+ return int(tp_path[1])
+ except ValueError:
+ return DEFAULT_TECH_PROFILE_TABLE_ID
+
+ def _create_tcont(self, uni_id, us_scheduler, tech_profile_id):
+ """
+ Decode Upstream Scheduler and create appropriate TCONT structures
+
+ :param uni_id: (int) UNI ID on the PON
+ :param us_scheduler: (Scheduler) Upstream Scheduler with TCONT information
+ :param tech_profile_id: (int) Tech Profile ID
+
+ :return (OnuTCont) Created TCONT
+ """
+ self.log.debug('create-tcont', us_scheduler=us_scheduler, profile_id=tech_profile_id)
+
+ q_sched_policy = {
+ 'strictpriority': 1, # Per TCONT (ME #262) values
+ 'wrr': 2
+ }.get(us_scheduler.get('q_sched_policy', 'none').lower(), 0)
+
+ tcont_data = {
+ 'tech-profile-id': tech_profile_id,
+ 'uni-id': uni_id,
+ 'alloc-id': us_scheduler['alloc_id'],
+ 'q-sched-policy': q_sched_policy
+ }
+ # TODO: Support TD if shaping on ONU is to be performed
+ td = OnuTrafficDescriptor(0, 0, 0)
+ tcont = OnuTCont.create(self, tcont_data, td)
+ self._pon.add_tcont(tcont)
+ return tcont
+
+ # Called when there is an olt up indication, providing the gem port id chosen by the olt handler
+ def _create_gemports(self, upstream_ports, downstream_ports, tcont, uni_id, tech_profile_id):
+ """
+ Create GEM Ports for a specifc tech profile
+
+ The routine will attempt to combine upstream and downstream GEM Ports into bidirectional
+ ports where possible
+
+ :param upstream_ports: (list of IGemPortAttribute) Upstream GEM Port attributes
+ :param downstream_ports: (list of IGemPortAttribute) Downstream GEM Port attributes
+ :param tcont: (OnuTCont) Associated TCONT
+ :param uni_id: (int) UNI Instance ID
+ :param tech_profile_id: (int) Tech Profile ID
+ """
+ self.log.debug('create-gemports', upstream=upstream_ports,
+ downstream_ports=downstream_ports,
+ tcont=tcont, tech_id=tech_profile_id)
+ # Convert GEM Port lists to dicts with GEM ID as they key
+ upstream = {gem['gemport_id']: gem for gem in upstream_ports}
+ downstream = {gem['gemport_id']: gem for gem in downstream_ports}
+
+ upstream_ids = set(upstream.keys())
+ downstream_ids = set(downstream.keys())
+ bidirectional_ids = upstream_ids & downstream_ids
+
+ gem_port_types = { # Keys are the 'direction' attribute value, value is list of GEM attributes
+ 1: [upstream[gid] for gid in upstream_ids - bidirectional_ids],
+ 2: [downstream[gid] for gid in downstream_ids - bidirectional_ids],
+ 3: [upstream[gid] for gid in bidirectional_ids]
+ }
+ for direction, gem_list in gem_port_types.items():
+ for gem in gem_list:
+ gem_data = {
+ 'gemport-id': gem['gemport_id'],
+ 'direction': direction,
+ 'encryption': gem['aes_encryption'].lower() == 'true',
+ 'discard-policy': gem['discard_policy'],
+ 'max-q-size': gem['max_q_size'],
+ 'pbit-map': gem['pbit_map'],
+ 'priority-q': gem['priority_q'],
+ 'scheduling_policy': gem['scheduling_policy'],
+ 'weight': gem['weight'],
+ 'uni-id': uni_id,
+ 'discard_config': {
+ 'max-probability': gem['discard_config']['max_probability'],
+ 'max-threshold': gem['discard_config']['max_threshold'],
+ 'min-threshold': gem['discard_config']['min_threshold'],
+ },
+ }
+ gem_port = OnuGemPort.create(self, gem_data,
+ tcont.alloc_id,
+ tech_profile_id,
+ self._pon.next_gem_entity_id)
+ self._pon.add_gem_port(gem_port)
+
+ def _do_tech_profile_configuration(self, uni_id, tp, tech_profile_id):
+ us_scheduler = tp['us_scheduler']
+ tcont = self._create_tcont(uni_id, us_scheduler, tech_profile_id)
+
+ upstream = tp['upstream_gem_port_attribute_list']
+ downstream = tp['downstream_gem_port_attribute_list']
+ self._create_gemports(upstream, downstream, tcont, uni_id, tech_profile_id)
+
+ def load_and_configure_tech_profile(self, uni_id, tp_path):
+ self.log.debug("loading-tech-profile-configuration", uni_id=uni_id, tp_path=tp_path)
+
+ if uni_id not in self._tp_service_specific_task:
+ self._tp_service_specific_task[uni_id] = dict()
+
+ if uni_id not in self._tech_profile_download_done:
+ self._tech_profile_download_done[uni_id] = dict()
+
+ if tp_path not in self._tech_profile_download_done[uni_id]:
+ self._tech_profile_download_done[uni_id][tp_path] = False
+
+ if not self._tech_profile_download_done[uni_id][tp_path]:
+ try:
+ if tp_path in self._tp_service_specific_task[uni_id]:
+ self.log.info("tech-profile-config-already-in-progress",
+ tp_path=tp_path)
+ return
+
+ tp = self.kv_client[tp_path]
+ tp = ast.literal_eval(tp)
+ self.log.debug("tp-instance", tp=tp)
+
+ tech_profile_id = self._tp_path_to_tp_id(tp_path)
+ self._do_tech_profile_configuration(uni_id, tp, tech_profile_id)
+
+ def success(_results):
+ self.log.info("tech-profile-config-done-successfully")
+ device = self.adapter_agent.get_device(self.device_id)
+ device.reason = 'tech-profile-config-download-success'
+ self.adapter_agent.update_device(device)
+ if tp_path in self._tp_service_specific_task[uni_id]:
+ del self._tp_service_specific_task[uni_id][tp_path]
+ self._tech_profile_download_done[uni_id][tp_path] = True
+
+ def failure(_reason):
+ self.log.warn('tech-profile-config-failure-retrying', reason=_reason)
+ device = self.adapter_agent.get_device(self.device_id)
+ device.reason = 'tech-profile-config-download-failure-retrying'
+ self.adapter_agent.update_device(device)
+ if tp_path in self._tp_service_specific_task[uni_id]:
+ del self._tp_service_specific_task[uni_id][tp_path]
+ self._deferred = reactor.callLater(_STARTUP_RETRY_WAIT, self.load_and_configure_tech_profile,
+ uni_id, tp_path)
+
+ self.log.info('downloading-tech-profile-configuration')
+ tp_task = AdtnTpServiceSpecificTask(self.openomci.omci_agent, self, uni_id)
+
+ self._tp_service_specific_task[uni_id][tp_path] = tp_task
+ # self._deferred = self.openomci.onu_omci_device.task_runner.queue_task(tp_task)
+ # self._deferred.addCallbacks(success, failure)
+
+ except Exception as e:
+ self.log.exception("error-loading-tech-profile", e=e)
+ else:
+ self.log.info("tech-profile-config-already-done")
+
def update_pm_config(self, device, pm_config):
# TODO: This has not been tested
self.log.info('update_pm_config', pm_config=pm_config)
@@ -393,13 +588,13 @@
self._cancel_deferred()
reregister = False
- # try:
- # # Drop registration for adapter messages
- # reregister = True
- # self.adapter_agent.unregister_for_inter_adapter_messages()
- #
- # except KeyError:
- # reregister = False
+ try:
+ # Drop registration for adapter messages
+ reregister = True
+ self.adapter_agent.unregister_for_inter_adapter_messages()
+
+ except KeyError:
+ reregister = False
# Update the operational status to ACTIVATING and connect status to
# UNREACHABLE
@@ -548,9 +743,10 @@
assert self.logical_device_id, 'Invalid logical device ID'
# reestablish logical ports for each UNI
+ multi_uni = len(self.uni_ports) > 1
for uni in self.uni_ports:
self.adapter_agent.add_port(device.id, uni.get_port())
- uni.add_logical_port(uni.logical_port_number)
+ uni.add_logical_port(uni.logical_port_number, multi_uni)
device = self.adapter_agent.get_device(device.id)
device.oper_status = OperStatus.ACTIVE
@@ -592,23 +788,21 @@
pptp_entities = self.openomci.onu_omci_device.configuration.pptp_entities
device = self.adapter_agent.get_device(self.device_id)
+ multi_uni = len(pptp_entities) > 1
+ uni_id = 0
+
for entity_id, pptp in pptp_entities.items():
intf_id = self.proxy_address.channel_id
onu_id = self.proxy_address.onu_id
- uni_no_start = platform.mk_uni_port_num(intf_id, onu_id)
-
- working_port = self._next_port_number
- uni_no = uni_no_start + working_port # OpenFlow port number
+ uni_no = platform.mk_uni_port_num(intf_id, onu_id, uni_id=uni_id)
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)
+ mac_bridge_port_num = uni_id + 1
uni_port = UniPort.create(self, uni_name, uni_no, uni_name)
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)
-
+ uni_port.add_logical_port(uni_port.port_number, multi_uni)
self.log.debug("created-uni-port", uni=uni_port)
self.adapter_agent.add_port(device.id, uni_port.get_port())
@@ -637,128 +831,7 @@
pon_port)
self.adapter_agent.update_device(device)
uni_port.enabled = True
- # TODO: only one uni/pptp for now. flow bug in openolt
-
- def on_tcont_create(self, tcont):
- from onu_tcont import OnuTCont
-
- self.log.info('create-tcont')
-
- td = self.traffic_descriptors.get(tcont.get('td-ref'))
- traffic_descriptor = td['object'] if td is not None else None
- tcont['object'] = OnuTCont.create(self, tcont, traffic_descriptor)
-
- if self._pon is not None:
- self._pon.add_tcont(tcont['object'])
-
- return tcont
-
- def on_tcont_modify(self, tcont, update, diffs):
- valid_keys = ['td-ref'] # Modify of these keys supported
-
- invalid_key = next((key for key in diffs.keys() if key not in valid_keys), None)
- if invalid_key is not None:
- raise KeyError("TCONT leaf '{}' is read-only or write-once".format(invalid_key))
-
- tc = tcont.get('object')
- assert tc is not None, 'TCONT not found'
-
- update['object'] = tc
-
- if self._pon is not None:
- keys = [k for k in diffs.keys() if k in valid_keys]
-
- for k in keys:
- if k == 'td-ref':
- td = self.traffic_descriptors.get(update['td-ref'])
- if td is not None:
- self._pon.update_tcont_td(tcont['alloc-id'], td)
-
- return update
-
- def on_tcont_delete(self, tcont):
- if self._pon is not None:
- self._pon.remove_tcont(tcont['alloc-id'])
-
- return None
-
- def on_td_create(self, traffic_disc):
- from onu_traffic_descriptor import OnuTrafficDescriptor
-
- traffic_disc['object'] = OnuTrafficDescriptor.create(traffic_disc)
- return traffic_disc
-
- def on_td_modify(self, traffic_disc, update, diffs):
- from onu_traffic_descriptor import OnuTrafficDescriptor
-
- valid_keys = ['fixed-bandwidth',
- 'assured-bandwidth',
- 'maximum-bandwidth',
- 'priority',
- 'weight',
- 'additional-bw-eligibility-indicator']
- invalid_key = next((key for key in diffs.keys() if key not in valid_keys), None)
- if invalid_key is not None:
- raise KeyError("traffic-descriptor leaf '{}' is read-only or write-once".format(invalid_key))
-
- # New traffic descriptor
- update['object'] = OnuTrafficDescriptor.create(update)
-
- td_name = traffic_disc['name']
- tconts = {key: val for key, val in self.tconts.iteritems()
- if val['td-ref'] == td_name and td_name is not None}
-
- for tcont in tconts.itervalues():
- if self._pon is not None:
- self._pon.update_tcont_td(tcont['alloc-id'], update['object'])
-
- return update
-
- def on_td_delete(self, traffic_desc):
- # TD may be used by more than one TCONT. Only delete if the last one
-
- td_name = traffic_desc['name']
- num_tconts = len([val for val in self.tconts.itervalues()
- if val['td-ref'] == td_name and td_name is not None])
-
- return None if num_tconts <= 1 else traffic_desc
-
- def on_gemport_create(self, gem_port):
- from onu_gem_port import OnuGemPort
- assert self._pon is not None, 'No PON port'
-
- gem_port['object'] = OnuGemPort.create(self, gem_port,
- self._pon.next_gem_entity_id)
- self._pon.add_gem_port(gem_port['object'])
- return gem_port
-
- def on_gemport_modify(self, gem_port, update, diffs):
- valid_keys = ['encryption',
- 'traffic-class'] # Modify of these keys supported
-
- invalid_key = next((key for key in diffs.keys() if key not in valid_keys), None)
- if invalid_key is not None:
- raise KeyError("GEM Port leaf '{}' is read-only or write-once".format(invalid_key))
-
- port = gem_port.get('object')
- assert port is not None, 'GemPort not found'
-
- keys = [k for k in diffs.keys() if k in valid_keys]
- update['object'] = port
-
- for k in keys:
- if k == 'encryption':
- port.encryption = update[k]
- elif k == 'traffic-class':
- pass # TODO: Implement
-
- return update
-
- def on_gemport_delete(self, gem_port):
- if self._pon is not None:
- self._pon.remove_gem_id(gem_port['gemport-id'])
-
- return None
+ uni_id += 1
def rx_inter_adapter_message(self, msg):
raise NotImplemented('Not currently supported')
diff --git a/voltha/adapters/adtran_onu/omci/adtn_tp_service_specific_task.py b/voltha/adapters/adtran_onu/omci/adtn_tp_service_specific_task.py
new file mode 100644
index 0000000..7dd375a
--- /dev/null
+++ b/voltha/adapters/adtran_onu/omci/adtn_tp_service_specific_task.py
@@ -0,0 +1,464 @@
+#
+# 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 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 *
+from voltha.adapters.brcm_openomci_onu.uni_port import *
+from voltha.adapters.brcm_openomci_onu.pon_port \
+ import BRDCM_DEFAULT_VLAN, TASK_PRIORITY, DEFAULT_TPID, DEFAULT_GEM_PAYLOAD
+
+OP = EntityOperations
+RC = ReasonCodes
+
+
+class TechProfileDownloadFailure(Exception):
+ """
+ This error is raised by default when the download fails
+ """
+
+
+class TechProfileResourcesFailure(Exception):
+ """
+ This error is raised by when one or more resources required is not available
+ """
+
+
+class AdtnTpServiceSpecificTask(Task):
+ """
+ Adtran OpenOMCI Tech-Profile Download Task
+ """
+ name = "Adtran Tech-Profile Download Task"
+
+ def __init__(self, omci_agent, handler, uni_id):
+ """
+ Class initialization
+
+ :param omci_agent: (OmciAdapterAgent) OMCI Adapter agent
+ :param device_id: (str) ONU Device ID
+ """
+ log = structlog.get_logger(device_id=handler.device_id, uni_id=uni_id)
+ log.debug('function-entry')
+
+ super(AdtnTpServiceSpecificTask, self).__init__(AdtnTpServiceSpecificTask.name,
+ omci_agent,
+ handler.device_id,
+ priority=TASK_PRIORITY,
+ exclusive=False)
+ self.log = log
+
+ self._onu_device = omci_agent.get_device(handler.device_id)
+ self._local_deferred = None
+
+ pon_port = handler.pon_port()
+ self._uni_port = handler.uni_ports[uni_id]
+ assert self._uni_port.uni_id == uni_id
+
+ # Port numbers
+ self._input_tpid = DEFAULT_TPID
+ self._output_tpid = DEFAULT_TPID
+
+ self._vlan_tcis_1 = BRDCM_DEFAULT_VLAN
+ self._cvid = BRDCM_DEFAULT_VLAN
+ self._vlan_config_entity_id = self._vlan_tcis_1
+ self._max_gem_payload = DEFAULT_GEM_PAYLOAD
+
+ # Entity IDs. IDs with values can probably be most anything for most ONUs,
+ # IDs set to None are discovered/set
+
+ self._mac_bridge_service_profile_entity_id = handler.mac_bridge_service_profile_entity_id
+ self._ieee_mapper_service_profile_entity_id = pon_port.ieee_mapper_service_profile_entity_id
+ self._mac_bridge_port_ani_entity_id = pon_port.mac_bridge_port_ani_entity_id
+ self._gal_enet_profile_entity_id = handler.gal_enet_profile_entity_id
+
+ # Extract the current set of TCONT and GEM Ports from the Handler's pon_port that are
+ # relevant to this task's UNI. It won't change. But, the underlying pon_port may change
+ # due to additional tasks on different UNIs. So, it we cannot use the pon_port affter
+ # this initializer
+ self._tconts = [tcont for tcont in pon_port.tconts.itervalues()
+ if tcont.uni_id == self._uni_port.uni_id]
+
+ self._gem_ports = [gem_port for gem_port in pon_port.gem_ports.itervalues()
+ if gem_port.uni_id != self._uni_port.uni_id]
+
+ self.tcont_me_to_queue_map = dict()
+ self.uni_port_to_queue_map = dict()
+
+ def cancel_deferred(self):
+ self.log.debug('function-entry')
+ super(AdtnTpServiceSpecificTask, 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 Tech-Profile Download
+ """
+ self.log.debug('function-entry')
+ super(AdtnTpServiceSpecificTask, self).start()
+ self._local_deferred = reactor.callLater(0, self.perform_service_specific_steps)
+
+ def stop(self):
+ """
+ Shutdown Tech-Profile download tasks
+ """
+ self.log.debug('function-entry')
+ self.log.debug('stopping')
+
+ self.cancel_deferred()
+ super(AdtnTpServiceSpecificTask, 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')
+
+ 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: %s", operation, omci_msg=omci_msg, status=status, error_mask=error_mask,
+ failed_mask=failed_mask, unsupported_mask=unsupported_mask)
+
+ if status == RC.Success:
+ self.strobe_watchdog()
+ return True
+
+ elif status == RC.InstanceExists:
+ return False
+
+ raise TechProfileDownloadFailure(
+ '{} failed with a status of {}, error_mask: {}, failed_mask: {}, unsupported_mask: {}'
+ .format(operation, status, error_mask, failed_mask, unsupported_mask))
+
+ @inlineCallbacks
+ def perform_service_specific_steps(self):
+ self.log.debug('function-entry')
+
+ omci_cc = self._onu_device.omci_cc
+
+ 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._tconts:
+ 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, alloc_id=tcont.alloc_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
+ results = yield tcont.add_to_hardware(omci_cc, free_entity_id)
+ self.check_status_and_state(results, 'create-tcont')
+
+ ################################################################################
+ # 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
+ #
+
+ onu_g = self._onu_device.query_mib(OntG.class_id)
+ # If the traffic management option attribute in the ONU-G ME is 0
+ # (priority controlled) or 2 (priority and rate controlled), this
+ # pointer specifies the priority queue ME serving this GEM port
+ # network CTP. If the traffic management option attribute is 1
+ # (rate controlled), this attribute redundantly points to the
+ # T-CONT serving this GEM port network CTP.
+ traffic_mgmt_opt = \
+ onu_g.get('attributes', {}).get('traffic_management_options', 0)
+ self.log.debug("traffic-mgmt-option", traffic_mgmt_opt=traffic_mgmt_opt)
+
+ prior_q = self._onu_device.query_mib(PriorityQueueG.class_id)
+ for k, v in prior_q.items():
+ self.log.debug("prior-q", k=k, v=v)
+
+ try:
+ _ = iter(v)
+ except TypeError:
+ continue
+
+ if 'instance_id' in v:
+ related_port = v['attributes']['related_port']
+ if v['instance_id'] & 0b1000000000000000:
+ tcont_me = (related_port & 0xffff0000) >> 16
+ if tcont_me not in self.tcont_me_to_queue_map:
+ self.log.debug("prior-q-related-port-and-tcont-me",
+ related_port=related_port,
+ tcont_me=tcont_me)
+ self.tcont_me_to_queue_map[tcont_me] = list()
+
+ self.tcont_me_to_queue_map[tcont_me].append(k)
+ else:
+ uni_port = (related_port & 0xffff0000) >> 16
+ if uni_port == self._uni_port.entity_id:
+ if uni_port not in self.uni_port_to_queue_map:
+ self.log.debug("prior-q-related-port-and-uni-port-me",
+ related_port=related_port,
+ uni_port_me=uni_port)
+ self.uni_port_to_queue_map[uni_port] = list()
+
+ self.uni_port_to_queue_map[uni_port].append(k)
+
+
+ self.log.debug("ul-prior-q", ul_prior_q=self.tcont_me_to_queue_map)
+ self.log.debug("dl-prior-q", dl_prior_q=self.uni_port_to_queue_map)
+
+ for gem_port in self._gem_ports:
+ # TODO: Traffic descriptor will be available after meter bands are available
+ tcont = gem_port.tcont
+ if tcont is None:
+ self.log.error('unknown-tcont-reference', gem_id=gem_port.gem_id)
+ continue
+
+ ul_prior_q_entity_id = None
+ dl_prior_q_entity_id = None
+ if gem_port.direction == "upstream" or \
+ gem_port.direction == "bi-directional":
+
+ # Sort the priority queue list in order of priority.
+ # 0 is highest priority and 0x0fff is lowest.
+ self.tcont_me_to_queue_map[tcont.entity_id].sort()
+ self.uni_port_to_queue_map[self._uni_port.entity_id].sort()
+ # Get the priority queue associated with p-bit that is
+ # mapped to the gem port.
+ # p-bit-7 is highest priority and p-bit-0 is lowest
+ # Gem port associated with p-bit-7 should be mapped to
+ # highest priority queue and gem port associated with p-bit-0
+ # should be mapped to lowest priority queue.
+ # The self.tcont_me_to_queue_map and self.uni_port_to_queue_map
+ # have priority queue entities ordered in descending order
+ # of priority
+ for i, p in enumerate(gem_port.pbit_map):
+ if p == '1':
+ ul_prior_q_entity_id = \
+ self.tcont_me_to_queue_map[tcont.entity_id][i]
+ dl_prior_q_entity_id = \
+ self.uni_port_to_queue_map[self._uni_port.entity_id][i]
+ break
+
+ assert ul_prior_q_entity_id is not None and \
+ dl_prior_q_entity_id is not None
+
+ # TODO: Need to restore on failure. Need to check status/results
+ results = yield gem_port.add_to_hardware(omci_cc,
+ tcont.entity_id,
+ self._ieee_mapper_service_profile_entity_id +
+ self._uni_port.mac_bridge_port_num,
+ self._gal_enet_profile_entity_id,
+ ul_prior_q_entity_id, dl_prior_q_entity_id)
+ self.check_status_and_state(results, 'create-gem-port')
+ elif gem_port.direction == "downstream":
+ # Downstream is inverse of upstream
+ # TODO: could also be a case of multicast. Not supported for now
+ pass
+
+ ################################################################################
+ # 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
+ #
+
+ gem_entity_ids = [OmciNullPointer] * 8
+ for gem_port in self._gem_ports:
+ self.log.debug("tp-gem-port", entity_id=gem_port.entity_id, uni_id=gem_port.uni_id)
+
+ if gem_port.direction == "upstream" or \
+ gem_port.direction == "bi-directional":
+ for i, p in enumerate(gem_port.pbit_map):
+ if p == '1':
+ gem_entity_ids[i] = gem_port.entity_id
+ elif gem_port.direction == "downstream":
+ # Downstream gem port p-bit mapper is inverse of upstream
+ # TODO: Could also be a case of multicast. Not supported for now
+ pass
+
+ msg = Ieee8021pMapperServiceProfileFrame(
+ self._ieee_mapper_service_profile_entity_id + self._uni_port.mac_bridge_port_num, # 802.1p mapper Service Mapper Profile ID
+ interwork_tp_pointers=gem_entity_ids # Interworking TP IDs
+ )
+ frame = msg.set()
+ self.log.debug('openomci-msg', omci_msg=msg)
+ results = yield omci_cc.send(frame)
+ self.check_status_and_state(results, 'set-8021p-mapper-service-profile-ul')
+
+ ################################################################################
+ # 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 or VEIP UNI
+ #
+
+ # TODO: do this for all uni/ports...
+ # TODO: magic. static variable for assoc_type
+
+ # default to PPTP
+ if self._uni_port.type is UniType.VEIP:
+ association_type = 10
+ elif self._uni_port.type is UniType.PPTP:
+ association_type = 2
+ else:
+ association_type = 2
+
+ attributes = dict(
+ association_type=association_type, # Assoc Type, PPTP/VEIP Ethernet UNI
+ associated_me_pointer=self._uni_port.entity_id, # Assoc ME, PPTP/VEIP Entity Id
+
+ # See VOL-1311 - Need to set table during create to avoid exception
+ # trying to read back table during post-create-read-missing-attributes
+ # But, because this is a R/W attribute. Some ONU may not accept the
+ # value during create. It is repeated again in a set below.
+ input_tpid=self._input_tpid, # input TPID
+ output_tpid=self._output_tpid, # output TPID
+ )
+
+ msg = ExtendedVlanTaggingOperationConfigurationDataFrame(
+ self._mac_bridge_service_profile_entity_id + self._uni_port.mac_bridge_port_num, # Bridge Entity ID
+ attributes=attributes
+ )
+
+ frame = msg.create()
+ self.log.debug('openomci-msg', omci_msg=msg)
+ results = yield omci_cc.send(frame)
+ self.check_status_and_state(results, 'create-extended-vlan-tagging-operation-configuration-data')
+
+ attributes = dict(
+ # Specifies the TPIDs in use and that operations in the downstream direction are
+ # inverse to the operations in the upstream direction
+ 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 + self._uni_port.mac_bridge_port_num, # Bridge Entity ID
+ attributes=attributes
+ )
+
+ frame = msg.set()
+ self.log.debug('openomci-msg', omci_msg=msg)
+ results = yield omci_cc.send(frame)
+ self.check_status_and_state(results, 'set-extended-vlan-tagging-operation-configuration-data')
+
+ attributes = dict(
+ # 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?
+ 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 + self._uni_port.mac_bridge_port_num, # Bridge Entity ID
+ attributes=attributes
+ )
+
+ frame = msg.set()
+ self.log.debug('openomci-msg', omci_msg=msg)
+ results = yield omci_cc.send(frame)
+ self.check_status_and_state(results, 'set-extended-vlan-tagging-operation-configuration-data-table')
+
+ self.deferred.callback("tech-profile-download-success")
+
+ except TimeoutError as e:
+ self.log.warn('rx-timeout-2', e=e)
+ self.deferred.errback(failure.Failure(e))
+
+ except Exception as e:
+ self.log.exception('omci-setup-2', e=e)
+ self.deferred.errback(failure.Failure(e))
diff --git a/voltha/adapters/adtran_onu/omci/omci.py b/voltha/adapters/adtran_onu/omci/omci.py
index 6e0ed82..a9d44c0 100644
--- a/voltha/adapters/adtran_onu/omci/omci.py
+++ b/voltha/adapters/adtran_onu/omci/omci.py
@@ -41,7 +41,6 @@
self._enabled = False
self._connected = False
self._deferred = None
- self._resync_deferred = None # For TCont/GEM use
self._bridge_initialized = False
self._in_sync_reached = False
self._omcc_version = OMCCVersion.Unknown
@@ -107,24 +106,15 @@
def _cancel_deferred(self):
d1, self._deferred = self._deferred, None
- d2, self._resync_deferred = self._resync_deferred, None
- d3, self._mib_download_deferred = self._mib_download_deferred, None
+ d2, self._mib_download_deferred = self._mib_download_deferred, None
- for d in [d1, d2, d3]:
+ for d in [d1, d2]:
try:
if d is not None and not d.called:
d.cancel()
except:
pass
- def _cancel_resync_deferred(self):
- d, self._resync_deferred = self._resync_deferred, None
- try:
- if d is not None and not d.called:
- d.cancel()
- except:
- pass
-
def delete(self):
self.enabled = False
@@ -247,11 +237,6 @@
self.log.exception('device-info-load', e=e)
self._deferred = reactor.callLater(_STARTUP_RETRY_WAIT, self._mib_in_sync)
- def gem_or_tcont_added(self):
- if self._in_sync_reached:
- self._cancel_resync_deferred()
- # TODO: Need to configure service here
-
def _subscribe_to_events(self):
from voltha.extensions.omci.onu_device_entry import OnuDeviceEvents, \
OnuDeviceEntry
diff --git a/voltha/adapters/adtran_onu/omci/omci_entities.py b/voltha/adapters/adtran_onu/omci/omci_entities.py
index e982ee7..654078d 100644
--- a/voltha/adapters/adtran_onu/omci/omci_entities.py
+++ b/voltha/adapters/adtran_onu/omci/omci_entities.py
@@ -15,9 +15,13 @@
""" Adtran vendor-specific OMCI Entities"""
import inspect
-
import sys
-from scapy.fields import ShortField, IntField, ByteField, StrFixedLenField
+import json
+from binascii import hexlify
+from bitstring import BitArray
+from scapy.fields import ByteField, ShortField, BitField
+from scapy.fields import IntField, StrFixedLenField, FieldListField, PacketLenField
+from scapy.packet import Packet
from voltha.extensions.omci.omci_entities import EntityClassAttribute, \
AttributeAccess, EntityOperations, EntityClass
@@ -128,6 +132,118 @@
mandatory_operations = {OP.Get, OP.Set, OP.GetNext}
+class AdtnVlanTaggingOperation(Packet):
+ name = "VlanTaggingOperation"
+ fields_desc = [
+ BitField("filter_outer_priority", 0, 4),
+ BitField("filter_outer_vid", 0, 13),
+ BitField("filter_outer_tpid_de", 0, 3),
+ BitField("pad1", 0, 12),
+
+ BitField("filter_inner_priority", 0, 4),
+ BitField("filter_inner_vid", 0, 13),
+ BitField("filter_inner_tpid_de", 0, 3),
+ BitField("pad2", 0, 8),
+ BitField("filter_ether_type", 0, 4),
+
+ BitField("treatment_tags_to_remove", 0, 2),
+ BitField("pad3", 0, 10),
+ BitField("treatment_outer_priority", 0, 4),
+ BitField("treatment_outer_vid", 0, 13),
+ BitField("treatment_outer_tpid_de", 0, 3),
+
+ BitField("pad4", 0, 12),
+ BitField("treatment_inner_priority", 0, 4),
+ BitField("treatment_inner_vid", 0, 13),
+ BitField("treatment_inner_tpid_de", 0, 3),
+ ]
+
+ def to_json(self):
+ return json.dumps(self.fields, separators=(',', ':'))
+
+ @staticmethod
+ def json_from_value(value):
+ bits = BitArray(hex=hexlify(value))
+ temp = AdtnVlanTaggingOperation(
+ filter_outer_priority=bits[0:4].uint, # 4 <-size
+ filter_outer_vid=bits[4:17].uint, # 13
+ filter_outer_tpid_de=bits[17:20].uint, # 3
+ # pad 12
+ filter_inner_priority=bits[32:36].uint, # 4
+ filter_inner_vid=bits[36:49].uint, # 13
+ filter_inner_tpid_de=bits[49:52].uint, # 3
+ # pad 8
+ filter_ether_type=bits[60:64].uint, # 4
+ treatment_tags_to_remove=bits[64:66].uint, # 2
+ # pad 10
+ treatment_outer_priority=bits[76:80].uint, # 4
+ treatment_outer_vid=bits[80:93].uint, # 13
+ treatment_outer_tpid_de=bits[93:96].uint, # 3
+ # pad 12
+ treatment_inner_priority=bits[108:112].uint, # 4
+ treatment_inner_vid=bits[112:125].uint, # 13
+ treatment_inner_tpid_de=bits[125:128].uint, # 3
+ )
+ return json.dumps(temp.fields, separators=(',', ':'))
+
+ def index(self):
+ return '{:02}'.format(self.fields.get('filter_outer_priority',0)) + \
+ '{:03}'.format(self.fields.get('filter_outer_vid',0)) + \
+ '{:01}'.format(self.fields.get('filter_outer_tpid_de',0)) + \
+ '{:03}'.format(self.fields.get('filter_inner_priority',0)) + \
+ '{:04}'.format(self.fields.get('filter_inner_vid',0)) + \
+ '{:01}'.format(self.fields.get('filter_inner_tpid_de',0)) + \
+ '{:02}'.format(self.fields.get('filter_ether_type',0))
+
+ def is_delete(self):
+ return self.fields.get('treatment_tags_to_remove',0) == 0x3 and \
+ self.fields.get('pad3',0) == 0x3ff and \
+ self.fields.get('treatment_outer_priority',0) == 0xf and \
+ self.fields.get('treatment_outer_vid',0) == 0x1fff and \
+ self.fields.get('treatment_outer_tpid_de',0) == 0x7 and \
+ self.fields.get('pad4',0) == 0xfff and \
+ self.fields.get('treatment_inner_priority',0) == 0xf and \
+ self.fields.get('treatment_inner_vid',0) == 0x1fff and \
+ self.fields.get('treatment_inner_tpid_de',0) == 0x7
+
+ def delete(self):
+ self.fields['treatment_tags_to_remove'] = 0x3
+ self.fields['pad3'] = 0x3ff
+ self.fields['treatment_outer_priority'] = 0xf
+ self.fields['treatment_outer_vid'] = 0x1fff
+ self.fields['treatment_outer_tpid_de'] = 0x7
+ self.fields['pad4'] = 0xfff
+ self.fields['treatment_inner_priority'] = 0xf
+ self.fields['treatment_inner_vid'] = 0x1fff
+ self.fields['treatment_inner_tpid_de'] = 0x7
+ return self
+
+
+class AdtnExtendedVlanTaggingOperationConfigurationData(EntityClass):
+ class_id = 171
+ attributes = [
+ ECA(ShortField("managed_entity_id", None), {AA.R, AA.SBC}),
+ ECA(ByteField("association_type", None), {AA.R, AA.W, AA.SBC},
+ range_check=lambda x: 0 <= x <= 11),
+ ECA(ShortField("received_vlan_tagging_operation_table_max_size", None),
+ {AA.R}),
+ ECA(ShortField("input_tpid", None), {AA.R, AA.W}),
+ ECA(ShortField("output_tpid", None), {AA.R, AA.W}),
+ ECA(ByteField("downstream_mode", None), {AA.R, AA.W},
+ range_check=lambda x: 0 <= x <= 8),
+ ECA(StrFixedLenField("received_frame_vlan_tagging_operation_table",
+ AdtnVlanTaggingOperation, 16), {AA.R, AA.W}),
+ ECA(ShortField("associated_me_pointer", None), {AA.R, AA.W, AA.SBC}),
+ ECA(FieldListField("dscp_to_p_bit_mapping", None,
+ BitField('', 0, size=3), count_from=lambda _: 64),
+ {AA.R, AA.W}),
+ ]
+ mandatory_operations = {OP.Create, OP.Delete, OP.Set, OP.Get, OP.GetNext}
+ optional_operations = {OP.SetTable}
+
+
+
+
#################################################################################
# entity class lookup table from entity_class values
_onu_entity_classes_name_map = dict(
diff --git a/voltha/adapters/adtran_onu/onu_gem_port.py b/voltha/adapters/adtran_onu/onu_gem_port.py
index d2c3dad..cb5ae45 100644
--- a/voltha/adapters/adtran_onu/onu_gem_port.py
+++ b/voltha/adapters/adtran_onu/onu_gem_port.py
@@ -24,25 +24,25 @@
"""
Adtran ONU specific implementation
"""
- def __init__(self, gem_id, alloc_id, entity_id,
- encryption=False,
- omci_transport=False,
+ def __init__(self, gem_data, alloc_id, tech_profile_id, entity_id,
multicast=False,
- tcont_ref=None,
traffic_class=None,
- name=None,
- handler=None):
- super(OnuGemPort, self).__init__(gem_id, alloc_id,
+ handler=None,
+ is_mock=False):
+ gem_id = gem_data['gemport-id']
+ encryption = gem_data['encryption']
+ super(OnuGemPort, self).__init__(gem_id, alloc_id, tech_profile_id,
encryption=encryption,
- omci_transport=omci_transport,
+ omci_transport=False,
multicast=multicast,
- tcont_ref=tcont_ref,
traffic_class=traffic_class,
- name=name,
- handler=handler)
+ handler=handler,
+ is_mock=is_mock)
+ self._gem_data = gem_data
self._entity_id = entity_id
self._tcont_entity_id = None
self._interworking = False
+ self.uni_id = gem_data['uni-id']
self.log = structlog.get_logger(device_id=handler.device_id, gem_id=gem_id)
@property
@@ -50,29 +50,18 @@
return self._entity_id
@property
- def encryption(self):
- return self._encryption
-
- @property
def in_hardware(self):
return self._tcont_entity_id is not None and self._interworking
- @encryption.setter
- def encryption(self, value):
- assert isinstance(value, bool), 'encryption is a boolean'
-
- if self._encryption != value:
- self._encryption = value
-
@staticmethod
- def create(handler, gem_port, entity_id):
- return OnuGemPort(gem_port['gemport-id'],
- None,
+ def create(handler, gem_data, alloc_id, tech_profile_id, entity_id):
+ # TODO: Only a minimal amount of info from the 'gem_port' dictionary
+ # is currently used to create the GEM ports.
+
+ return OnuGemPort(gem_data,
+ alloc_id,
+ tech_profile_id,
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)
@inlineCallbacks
@@ -80,6 +69,8 @@
tcont_entity_id,
ieee_mapper_service_profile_entity_id,
gal_enet_profile_entity_id):
+ if self._is_mock:
+ returnValue('mock')
self.log.debug('add-to-hardware', gem_id=self.gem_id,
gem_entity_id=self.entity_id,
@@ -154,6 +145,9 @@
@inlineCallbacks
def remove_from_hardware(self, omci):
+ if self._is_mock:
+ returnValue('mock')
+
self.log.debug('remove-from-hardware', gem_id=self.gem_id)
results = None
diff --git a/voltha/adapters/adtran_onu/onu_tcont.py b/voltha/adapters/adtran_onu/onu_tcont.py
index dee3fcc..3031ca8 100644
--- a/voltha/adapters/adtran_onu/onu_tcont.py
+++ b/voltha/adapters/adtran_onu/onu_tcont.py
@@ -28,11 +28,14 @@
free_tcont_alloc_id = 0xFFFF
free_gpon_tcont_alloc_id = 0xFF # SFU may use this to indicate a free TCONT
- def __init__(self, handler, alloc_id, traffic_descriptor, name=None):
- super(OnuTCont, self).__init__(alloc_id, traffic_descriptor, name=name)
- self._handler = handler
- self._entity_id = None
+ def __init__(self, handler, alloc_id, sched_policy, tech_profile_id, uni_id, traffic_descriptor, is_mock=False):
+ super(OnuTCont, self).__init__(alloc_id, tech_profile_id, traffic_descriptor, is_mock=is_mock)
self.log = structlog.get_logger(device_id=handler.device_id, alloc_id=alloc_id)
+
+ self._handler = handler
+ self.sched_policy = sched_policy
+ self.uni_id = uni_id
+ self._entity_id = None
self._free_alloc_id = OnuTCont.free_tcont_alloc_id
@property
@@ -43,11 +46,18 @@
def create(handler, tcont, td):
assert isinstance(tcont, dict), 'TCONT should be a dictionary'
assert isinstance(td, TrafficDescriptor), 'Invalid Traffic Descriptor data type'
- return OnuTCont(handler, tcont['alloc-id'], td, name=tcont['name'])
+ return OnuTCont(handler,
+ tcont['alloc-id'],
+ tcont['q-sched-policy'],
+ tcont['tech-profile-id'],
+ tcont['uni-id'],
+ td)
@inlineCallbacks
def add_to_hardware(self, omci, tcont_entity_id, prev_alloc_id=free_tcont_alloc_id):
self.log.debug('add-to-hardware', tcont_entity_id=tcont_entity_id)
+ if self._is_mock:
+ returnValue('mock')
if self._entity_id == tcont_entity_id:
returnValue('Already set')
@@ -56,6 +66,9 @@
raise KeyError('TCONT already assigned: {}'.format(self.entity_id))
try:
+ # TODO: Look up ONU2-G QoS flexibility attribute and only set this
+ # if it can be supported
+
self._free_alloc_id = prev_alloc_id
frame = TcontFrame(tcont_entity_id, self.alloc_id).set()
results = yield omci.send(frame)
@@ -80,6 +93,8 @@
@inlineCallbacks
def remove_from_hardware(self, omci):
self.log.debug('remove-from-hardware', tcont_entity_id=self.entity_id)
+ if self._is_mock:
+ returnValue('mock')
try:
frame = TcontFrame(self.entity_id, self._free_alloc_id).set()
results = yield omci.send(frame)
diff --git a/voltha/adapters/adtran_onu/onu_traffic_descriptor.py b/voltha/adapters/adtran_onu/onu_traffic_descriptor.py
index 894959b..c09b0a6 100644
--- a/voltha/adapters/adtran_onu/onu_traffic_descriptor.py
+++ b/voltha/adapters/adtran_onu/onu_traffic_descriptor.py
@@ -23,12 +23,10 @@
"""
def __init__(self, fixed, assured, maximum,
additional=TrafficDescriptor.AdditionalBwEligibility.DEFAULT,
- best_effort=None,
- name=None):
+ best_effort=None):
super(OnuTrafficDescriptor, self).__init__(fixed, assured, maximum,
additional=additional,
- best_effort=best_effort,
- name=name)
+ best_effort=best_effort)
@staticmethod
def create(traffic_disc):
@@ -47,7 +45,6 @@
return OnuTrafficDescriptor(traffic_disc['fixed-bandwidth'],
traffic_disc['assured-bandwidth'],
traffic_disc['maximum-bandwidth'],
- name=traffic_disc['name'],
best_effort=best_effort,
additional=additional)
diff --git a/voltha/adapters/adtran_onu/pon_port.py b/voltha/adapters/adtran_onu/pon_port.py
index d82eff9..4f00c0d 100644
--- a/voltha/adapters/adtran_onu/pon_port.py
+++ b/voltha/adapters/adtran_onu/pon_port.py
@@ -184,27 +184,6 @@
self.log.info('add', tcont=tcont, reflow=reflow)
self._tconts[tcont.alloc_id] = tcont
- # TODO: Refactor once xPON goes away
- self._handler.openomci.gem_or_tcont_added()
-
- def update_tcont_td(self, alloc_id, new_td):
- 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):
tcont = self._tconts.get(alloc_id)
@@ -246,9 +225,6 @@
self.log.info('add', gem_port=gem_port, reflow=reflow)
self._gem_ports[gem_port.gem_id] = gem_port
- # TODO: Refactor once xPON goes away
- self._handler.openomci.gem_or_tcont_added()
-
@inlineCallbacks
def remove_gem_id(self, gem_id):
"""
diff --git a/voltha/adapters/adtran_onu/uni_port.py b/voltha/adapters/adtran_onu/uni_port.py
index 0f2c10f..5194282 100644
--- a/voltha/adapters/adtran_onu/uni_port.py
+++ b/voltha/adapters/adtran_onu/uni_port.py
@@ -20,6 +20,7 @@
from voltha.protos.logical_device_pb2 import LogicalPort
from voltha.protos.openflow_13_pb2 import OFPPS_LIVE, OFPPF_FIBER
from voltha.protos.openflow_13_pb2 import ofp_port
+import voltha.adapters.adtran_olt.resources.adtranolt_platform as platform
class UniPort(object):
@@ -29,6 +30,7 @@
self._enabled = False
self._handler = handler
self._name = name
+ self.uni_id = platform.uni_id_from_uni_port(port_no)
self._port = None
self._port_number = port_no
self._ofp_port_no = ofp_port_no # Set at by creator (vENET create)
@@ -162,16 +164,16 @@
"""
if self._port is None:
self._port = Port(port_no=self.port_number,
- label='vEth-{}'.format(self.port_number),
+ label=self.port_id_name(),
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)
+ return 'uni-{}'.format(self._port_number)
- def add_logical_port(self, openflow_port_no,
+ def add_logical_port(self, openflow_port_no, multi_uni_naming,
capabilities=OFPPF_10GB_FD | OFPPF_FIBER,
speed=OFPPF_10GB_FD):
@@ -202,7 +204,7 @@
device.parent_port_no & 0xff,
(port_no >> 8) & 0xff,
port_no & 0xff)),
- name=device.serial_number,
+ name=device.serial_number + ['', '-' + str(self._mac_bridge_port_num)][multi_uni_naming],
config=0,
state=OFPPS_LIVE,
curr=capabilities,