VOL-1238: Adtran OLT initial support of Resource Manager
Includes some significant cleanup of deprecated xPON functionality
and stale v1.0 features
Change-Id: I7f5a48a49e04c857699b39c7f6336352d13fbfd6
diff --git a/voltha/adapters/adtran_olt/README.md b/voltha/adapters/adtran_olt/README.md
index 9e109a7..737b73b 100644
--- a/voltha/adapters/adtran_olt/README.md
+++ b/voltha/adapters/adtran_olt/README.md
@@ -14,9 +14,8 @@
| -T | --rc_port | 8081 | REST TCP Port |
| -z | --zmq_port | 5656 | ZeroMQ OMCI Proxy Port |
| -M | --multicast_vlan | 4000 | Multicast VLANs (comma-delimited) |
-| -v | --untagged_vlan | 4092 | VLAN wrapper for untagged ONU frames |
| -Z | --pio_port | 5657 | PIO Service ZeroMQ Port |
-| -X | --xpon_enable | False | Support BBF WT-386 xPON CLI/NBI provisioning |
+| -o | --resource_mgr_key | adtran_olt | OLT Type to look up associated resource manager configuration |
For example, if your Adtran OLT is address 10.17.174.193 with the default TCP ports and
NETCONF credentials of admin/admin and REST credentials of ADMIN/ADMIN, the command line
@@ -43,6 +42,57 @@
preprovision_olt -t adtran_olt --host_and_port 10.17.174.193:830
```
+## Resource Manager Provisioning Support
+Starting in Fall of 2018, Resource Manager Support was added as the default provisioning mechanism
+for the Adtran OLT as the xPON provisioning support will be deprecated by the v2.0 release in
+late-2018/early-2019.
+
+The Resource Manager is used to manage device PON resource pool and allocate PON resources from
+such pools. Resource Manager module currently manages assignment of ONU-ID, ALLOC-ID and
+GEM-PORT ID. The Resource Manager uses the KV store to back-up all the resource pool allocation data.
+
+The Adtran OLT adapter interacts with Resource Manager module for PON resource assignments. The
+adtranolt_resource_manager module is responsible for interfacing with the Resource Manager.
+
+The Resource Manager optionally uses olt_vendor_type specific resource ranges to initialize the
+PON resource pools. In order to utilize this option, create an entry for olt_vendor_type specific
+PON resource ranges on the KV store. Please make sure to use the same KV store used by the VOLTHA core.
+
+### For example
+To specify **ADTRAN OLT** device specific resource ranges, first create a JSON file
+_adtran_olt_resource_range.json_ with the following entry
+
+{
+ "onu_start_idx": 0,
+ "onu_end_idx": 127,
+ "alloc_id_start_idx": 1024,
+ "alloc_id_end_idx": 4222,
+ "gem_port_id_start_idx": 2176,
+ "gem_port_id_end_idx": 16383,
+ "num_of_pon_port": 16
+}
+This data should be put on the KV store location _resource_manager/xgspon/resource_ranges/adtran_olt_
+
+The format of the KV store location is resource_manager/<technology>/resource_ranges/<resource_mgr_key>
+
+In the below example the KV store is assumed to be Consul. However the same is applicable to be
+etcd or any other KV store. Please make sure to use the same KV store used by the VOLTHA core.
+
+```bash
+curl -X PUT -H "Content-Type: application/json" \
+ http://127.0.0.1:8500/v1/kv/resource_manager/xgspon/resource_ranges/adtran_olt \
+ -d @./adtran_olt_resource_range.json
+```
+The olt_vendor_type should be referred to during the preprovisioning step as shown below. The
+olt_vendor_type is an extra option and should be specified after --. The -o specifies the resource_mgr_key.
+
+ (voltha) preprovision_olt -t adtran -H 192.168.1.100:830 -- -o adtran_olt
+Once the OLT device is enabled, any further PON Resource assignments will happen within the PON Resource ranges defined in asfvolt16_resource_range.json and placed on the KV store.
+
+Additional Notes
+If a default resource range profile should be used with all olt_vendor_types, then place such Resource Range profile at the below path on the KV store.
+
+resource_manager/xgspon/resource_ranges/default
## xPON Provisioning Support
diff --git a/voltha/adapters/adtran_olt/adtran_device_handler.py b/voltha/adapters/adtran_olt/adtran_device_handler.py
index 4740f93..ad735b3 100644
--- a/voltha/adapters/adtran_olt/adtran_device_handler.py
+++ b/voltha/adapters/adtran_olt/adtran_device_handler.py
@@ -19,7 +19,6 @@
import shlex
import time
-import arrow
import structlog
from twisted.internet import reactor, defer
from twisted.internet.defer import inlineCallbacks, returnValue
@@ -35,13 +34,13 @@
from voltha.extensions.alarms.adapter_alarms import AdapterAlarms
from voltha.extensions.kpi.olt.olt_pm_metrics import OltPmMetrics
from common.utils.asleep import asleep
+from flow.flow_tables import DeviceFlows, DownstreamFlows
_ = third_party
DEFAULT_MULTICAST_VLAN = 4000
-BROADCOM_UNTAGGED_VLAN = 4091 # SEBA - For BBWF demo (BroadCom Default VLAN)
-DEFAULT_UTILITY_VLAN = 4094
-DEFAULT_UNTAGGED_VLAN = BROADCOM_UNTAGGED_VLAN # if RG does not send priority tagged frames
+BROADCOM_UNTAGGED_VLAN = 4091
+DEFAULT_UTILITY_VLAN = BROADCOM_UNTAGGED_VLAN
_DEFAULT_RESTCONF_USERNAME = ""
_DEFAULT_RESTCONF_PASSWORD = ""
@@ -52,8 +51,7 @@
_DEFAULT_NETCONF_PORT = 830
_STARTUP_RETRY_TIMEOUT = 5 # 5 seconds delay after activate failed before we
-_DEFAULT_XPON_SUPPORTED = False # LOOK for the keywords 'xpon_support', SEBA
- # for areas to clean up once xPON is deprecated
+_DEFAULT_RESOURCE_MGR_KEY = "adtran"
class AdtranDeviceHandler(object):
@@ -107,11 +105,11 @@
self.pm_metrics = None
self.alarms = None
self.multicast_vlans = [DEFAULT_MULTICAST_VLAN]
- self.untagged_vlan = DEFAULT_UNTAGGED_VLAN
self.utility_vlan = DEFAULT_UTILITY_VLAN
self.mac_address = '00:13:95:00:00:00'
self._rest_support = None
self._initial_enable_complete = False
+ self.resource_mgr = None
# Northbound and Southbound ports
self.northbound_ports = {} # port number -> Port
@@ -123,6 +121,7 @@
# self.num_management_ports = None
self.ip_address = None
+ self.host_and_port = None
self.timeout = timeout
self.restart_failure_timeout = 5 * 60 # 5 Minute timeout
@@ -138,10 +137,14 @@
self.netconf_password = _DEFAULT_NETCONF_PASSWORD
self._netconf_client = None
- # TODO: Decrement xPON once Technology Profiles completed
- self.xpon_support = _DEFAULT_XPON_SUPPORTED
+ # Flow entries
+ self.upstream_flows = DeviceFlows()
+ self.downstream_flows = DownstreamFlows()
+
self.max_nni_ports = 1 # TODO: This is a VOLTHA imposed limit in 'flow_decomposer.py
# and logical_device_agent.py
+
+ self.resource_manager_key = _DEFAULT_RESOURCE_MGR_KEY
# OMCI ZMQ Channel
self.pon_agent_port = DEFAULT_PON_AGENT_TCP_PORT
self.pio_port = DEFAULT_PIO_TCP_PORT
@@ -226,11 +229,12 @@
if device.ipv4_address:
self.ip_address = device.ipv4_address
-
+ self.host_and_port = '{}:{}'.format(self.ip_address,
+ self.netconf_port)
elif device.host_and_port:
- host_and_port = device.host_and_port.split(":")
- self.ip_address = host_and_port[0]
- self.netconf_port = int(host_and_port[1])
+ self.host_and_port = device.host_and_port.split(":")
+ self.ip_address = self.host_and_port[0]
+ self.netconf_port = int(self.host_and_port[1])
self.adapter_agent.update_device(device)
else:
@@ -238,7 +242,6 @@
#############################################################
# Now optional parameters
-
def check_tcp_port(value):
ivalue = int(value)
if ivalue <= 0 or ivalue > 65535:
@@ -247,8 +250,8 @@
def check_vid(value):
ivalue = int(value)
- if ivalue <= 1 or ivalue > 4094:
- raise argparse.ArgumentTypeError("Valid VLANs are 2..4094")
+ if ivalue < 1 or ivalue > 4094:
+ raise argparse.ArgumentTypeError("Valid VLANs are 1..4094")
return ivalue
parser = argparse.ArgumentParser(description='Adtran Device Adapter')
@@ -256,14 +259,14 @@
help='NETCONF username')
parser.add_argument('--nc_password', '-p', action='store', default=_DEFAULT_NETCONF_PASSWORD,
help='NETCONF Password')
- parser.add_argument('--nc_port', '-t', action='store', default=_DEFAULT_NETCONF_PORT, type=check_tcp_port,
- help='NETCONF TCP Port')
+ parser.add_argument('--nc_port', '-t', action='store', default=_DEFAULT_NETCONF_PORT,
+ type=check_tcp_port, help='NETCONF TCP Port')
parser.add_argument('--rc_username', '-U', action='store', default=_DEFAULT_RESTCONF_USERNAME,
help='REST username')
parser.add_argument('--rc_password', '-P', action='store', default=_DEFAULT_RESTCONF_PASSWORD,
help='REST Password')
- parser.add_argument('--rc_port', '-T', action='store', default=_DEFAULT_RESTCONF_PORT, type=check_tcp_port,
- help='RESTCONF TCP Port')
+ parser.add_argument('--rc_port', '-T', action='store', default=_DEFAULT_RESTCONF_PORT,
+ type=check_tcp_port, help='RESTCONF TCP Port')
parser.add_argument('--zmq_port', '-z', action='store', default=DEFAULT_PON_AGENT_TCP_PORT,
type=check_tcp_port, help='PON Agent ZeroMQ Port')
parser.add_argument('--pio_port', '-Z', action='store', default=DEFAULT_PIO_TCP_PORT,
@@ -271,15 +274,12 @@
parser.add_argument('--multicast_vlan', '-M', action='store',
default='{}'.format(DEFAULT_MULTICAST_VLAN),
help='Multicast VLAN'),
- parser.add_argument('--untagged_vlan', '-v', action='store',
- default='{}'.format(DEFAULT_UNTAGGED_VLAN),
- help='VLAN for Untagged Frames from ONUs'),
parser.add_argument('--utility_vlan', '-B', action='store',
default='{}'.format(DEFAULT_UTILITY_VLAN),
- help='VLAN for Untagged Frames from ONUs')
- parser.add_argument('--xpon_enable', '-X', action='store_true',
- default=_DEFAULT_XPON_SUPPORTED,
- help='enable xPON (BBF WT-385) provisioning support')
+ type=check_vid, help='VLAN for Controller based upstream flows from ONUs')
+ parser.add_argument('--resource_mgr_key', '-o', action='store',
+ default=_DEFAULT_RESOURCE_MGR_KEY,
+ help='OLT Type to look up associated resource manager configuration')
try:
args = parser.parse_args(shlex.split(device.extra_args))
@@ -296,10 +296,7 @@
self.pon_agent_port = args.zmq_port
self.pio_port = args.pio_port
- self.xpon_support = args.xpon_enable
-
- if not self.xpon_support:
- self.untagged_vlan = BROADCOM_UNTAGGED_VLAN
+ self.resource_manager_key = args.resource_mgr_key
if not self.rest_username:
self.rest_username = 'NDE0NDRkNDk0ZQ==\n'.\
@@ -382,7 +379,6 @@
############################################################################
# Get the device Information
-
if reconciling:
device.connect_status = ConnectStatus.REACHABLE
self.adapter_agent.update_device(device)
@@ -410,7 +406,6 @@
try:
# Enumerate and create Northbound NNI interfaces
-
device.reason = 'enumerating northbound interfaces'
self.adapter_agent.update_device(device)
self.startup = self.enumerate_northbound_ports(device)
@@ -432,7 +427,6 @@
try:
# Enumerate and create southbound interfaces
-
device.reason = 'enumerating southbound interfaces'
self.adapter_agent.update_device(device)
self.startup = self.enumerate_southbound_ports(device)
@@ -452,6 +446,9 @@
self.log.exception('PON_enumeration', e=e)
returnValue(self.restart_activate(done_deferred, reconciling))
+ # Initialize resource manager
+ self.initialize_resource_manager()
+
if reconciling:
if device.admin_state == AdminState.ENABLED:
if device.parent_id:
@@ -469,7 +466,6 @@
else:
# Complete activation by setting up logical device for this OLT and saving
# off the devices parent_id
-
ld_initialized = self.create_logical_device(device)
############################################################################
@@ -525,7 +521,6 @@
############################################################################
# Setup Alarm handler
-
device.reason = 'setting up adapter alarms'
self.adapter_agent.update_device(device)
@@ -534,7 +529,6 @@
############################################################################
# Register for ONU detection
# self.adapter_agent.register_for_onu_detect_state(device.id)
-
# Complete device specific steps
try:
self.log.debug('device-activation-procedures')
@@ -734,8 +728,6 @@
self.startup = yield EVC.remove_all(self.netconf_client)
from flow.utility_evc import UtilityEVC
self.startup = yield UtilityEVC.remove_all(self.netconf_client)
- from flow.untagged_evc import UntaggedEVC
- self.startup = yield UntaggedEVC.remove_all(self.netconf_client)
except Exception as e:
self.log.exception('evc-cleanup', e=e)
@@ -756,7 +748,7 @@
self.log.exception('acl-cleanup', e=e)
from flow.flow_entry import FlowEntry
- FlowEntry.clear_all(device.id)
+ FlowEntry.clear_all(self)
from download import Download
Download.clear_all(self.netconf_client)
@@ -873,6 +865,9 @@
def get_port_name(self, port):
raise NotImplementedError('implement in derived class')
+ def initialize_resource_manager(self):
+ raise NotImplementedError('implement in derived class')
+
@inlineCallbacks
def complete_device_specific_activation(self, _device, _reconciling):
# NOTE: Override this in your derived class for any device startup completion
@@ -989,7 +984,9 @@
for port in self.southbound_ports.itervalues():
self.log.debug('reenable-checking-pon-port', pon_id=port.pon_id)
- gpon_info = self.get_xpon_info(port.pon_id) # SEBA
+ # TODO: Need to implement this now that XPON is deprecated
+ #gpon_info = self.get_xpon_info(port.pon_id) # SEBA
+ gpon_info = None
if gpon_info is not None and \
gpon_info['channel-terminations'] is not None and \
len(gpon_info['channel-terminations']) > 0:
@@ -1183,7 +1180,6 @@
self.adapter_agent.update_child_devices_state(self.device_id,
connect_status=ConnectStatus.REACHABLE)
# Restart ports to previous state
-
dl = []
for port in self.northbound_ports.itervalues():
@@ -1194,14 +1190,13 @@
try:
yield defer.gatherResults(dl, consumeErrors=True)
+
except Exception as e:
self.log.exception('port-restart', e=e)
# Re-subscribe for ONU detection
# self.adapter_agent.register_for_onu_detect_state(self.device.id)
-
# Request reflow of any EVC/EVC-MAPs
-
if len(self._evcs) > 0:
dl = []
for evc in self.evcs:
diff --git a/voltha/adapters/adtran_olt/adtran_olt.py b/voltha/adapters/adtran_olt/adtran_olt.py
index 9bbf497..ee2ede8 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.27',
+ version='1.29',
config=AdapterConfig(log_level=LogLevel.INFO)
)
log.debug('adtran_olt.__init__', adapter_agent=adapter_agent)
@@ -60,6 +60,11 @@
self.interface = registry('main').get_args().interface
self.logical_device_id_to_root_device_id = dict()
+ @property
+ def devices(self):
+ """ BroadCom adapter needs to reach in and mess with us. """
+ return self.devices_handlers
+
def start(self):
"""
Called once after adapter instance is loaded. Can be used to async
@@ -468,193 +473,49 @@
# PON Mgnt APIs #
def create_interface(self, device, data):
- """
- API to create various interfaces (only some PON interfaces as of now)
- in the devices
- """
- log.debug('create-interface', data=data)
- handler = self.devices_handlers.get(device.id)
- if handler is not None:
- handler.xpon_create(data)
+ raise NotImplemented('xPON has been deprecated')
def update_interface(self, device, data):
- """
- API to update various interfaces (only some PON interfaces as of now)
- in the devices
- """
- log.debug('update-interface', data=data)
- handler = self.devices_handlers.get(device.id)
- if handler is not None:
- handler.xpon_update(data)
+ raise NotImplemented('xPON has been deprecated')
def remove_interface(self, device, data):
- """
- API to delete various interfaces (only some PON interfaces as of now)
- in the devices
- """
- log.debug('remove-interface', data=data)
- handler = self.devices_handlers.get(device.id)
- if handler is not None:
- handler.xpon_remove(data)
+ raise NotImplemented('xPON has been deprecated')
def receive_onu_detect_state(self, proxy_address, state):
- """
- Receive onu detect state in ONU adapter
- :param proxy_address: ONU device address
- :param state: ONU detect state (bool)
- :return: None
- """
raise NotImplementedError()
def create_tcont(self, device, tcont_data, traffic_descriptor_data):
- """
- API to create tcont object in the devices
- :param device: device id
- :param tcont_data: tcont data object
- :param traffic_descriptor_data: traffic descriptor data object
- :return: None
- """
- log.info('create-tcont', tcont_data=tcont_data,
- traffic_descriptor_data=traffic_descriptor_data)
- handler = self.devices_handlers.get(device.id)
- if handler is not None:
- handler.create_tcont(tcont_data, traffic_descriptor_data)
+ raise NotImplemented('xPON has been deprecated')
def update_tcont(self, device, tcont_data, traffic_descriptor_data):
- """
- API to update tcont object in the devices
- :param device: device id
- :param tcont_data: tcont data object
- :param traffic_descriptor_data: traffic descriptor data object
- :return: None
- """
- log.info('update-tcont', tcont_data=tcont_data,
- traffic_descriptor_data=traffic_descriptor_data)
- handler = self.devices_handlers.get(device.id)
- if handler is not None:
- handler.update_tcont(tcont_data, traffic_descriptor_data)
+ raise NotImplemented('xPON has been deprecated')
def remove_tcont(self, device, tcont_data, traffic_descriptor_data):
- """
- API to delete tcont object in the devices
- :param device: device id
- :param tcont_data: tcont data object
- :param traffic_descriptor_data: traffic descriptor data object
- :return: None
- """
- log.info('remove-tcont', tcont_data=tcont_data,
- traffic_descriptor_data=traffic_descriptor_data)
- handler = self.devices_handlers.get(device.id)
- if handler is not None:
- handler.remove_tcont(tcont_data, traffic_descriptor_data)
+ raise NotImplemented('xPON has been deprecated')
def create_gemport(self, device, data):
- """
- API to create gemport object in the devices
- :param device: device id
- :param data: gemport data object
- :return: None
- """
- log.debug('create-gemport', data=data)
- handler = self.devices_handlers.get(device.id)
- if handler is not None:
- handler.xpon_create(data)
+ raise NotImplemented('xPON has been deprecated')
def update_gemport(self, device, data):
- """
- API to update gemport object in the devices
- :param device: device id
- :param data: gemport data object
- :return: None
- """
- log.info('update-gemport', data=data)
- handler = self.devices_handlers.get(device.id)
- if handler is not None:
- handler.xpon_update(data)
+ raise NotImplemented('xPON has been deprecated')
def remove_gemport(self, device, data):
- """
- API to delete gemport object in the devices
- :param device: device id
- :data: gemport data object
- :return: None
- """
- log.info('remove-gemport', data=data)
- handler = self.devices_handlers.get(device.id)
- if handler is not None:
- handler.xpon_remove(data)
+ raise NotImplemented('xPON has been deprecated')
def create_multicast_gemport(self, device, data):
- """
- API to create multicast gemport object in the devices
- :param device: device id
- :data: multicast gemport data object
- :return: None
- """
- log.info('create-mcast-gemport', data=data)
- handler = self.devices_handlers.get(device.id)
- if handler is not None:
- handler.xpon_create(data)
+ raise NotImplemented('xPON has been deprecated')
def update_multicast_gemport(self, device, data):
- """
- API to update multicast gemport object in the devices
- :param device: device id
- :data: multicast gemport data object
- :return: None
- """
- log.info('update-mcast-gemport', data=data)
- handler = self.devices_handlers.get(device.id)
- if handler is not None:
- handler.xpon_update(data)
+ raise NotImplemented('xPON has been deprecated')
def remove_multicast_gemport(self, device, data):
- """
- API to delete multicast gemport object in the devices
- :param device: device id
- :data: multicast gemport data object
- :return: None
- """
- log.info('remove-mcast-gemport', data=data)
- handler = self.devices_handlers.get(device.id)
- if handler is not None:
- handler.xpon_remove(data)
+ raise NotImplemented('xPON has been deprecated')
def create_multicast_distribution_set(self, device, data):
- """
- API to create multicast distribution rule to specify
- the multicast VLANs that ride on the multicast gemport
- :param device: device id
- :data: multicast distribution data object
- :return: None
- """
- log.info('create-mcast-distribution-set', data=data)
- handler = self.devices_handlers.get(device.id)
- if handler is not None:
- handler.xpon_create(data)
+ raise NotImplemented('xPON has been deprecated')
def update_multicast_distribution_set(self, device, data):
- """
- API to update multicast distribution rule to specify
- the multicast VLANs that ride on the multicast gemport
- :param device: device id
- :data: multicast distribution data object
- :return: None
- """
- log.info('update-mcast-distribution-set', data=data)
- handler = self.devices_handlers.get(device.id)
- if handler is not None:
- handler.xpon_update(data)
+ raise NotImplemented('xPON has been deprecated')
def remove_multicast_distribution_set(self, device, data):
- """
- API to delete multicast distribution rule to specify
- the multicast VLANs that ride on the multicast gemport
- :param device: device id
- :data: multicast distribution data object
- :return: None
- """
- log.info('remove-mcast-distribution-set', data=data)
- handler = self.devices_handlers.get(device.id)
- if handler is not None:
- handler.xpon_remove(data)
+ raise NotImplemented('xPON has been deprecated')
diff --git a/voltha/adapters/adtran_olt/adtran_olt_handler.py b/voltha/adapters/adtran_olt/adtran_olt_handler.py
index 462db97..2df0387 100644
--- a/voltha/adapters/adtran_olt/adtran_olt_handler.py
+++ b/voltha/adapters/adtran_olt/adtran_olt_handler.py
@@ -18,25 +18,23 @@
from twisted.internet import reactor
from twisted.internet.defer import returnValue, inlineCallbacks, succeed
-from voltha.protos.device_pb2 import Port
from adtran_device_handler import AdtranDeviceHandler
-import adtranolt_platform as platform
+from voltha.adapters.adtran_olt.resources import adtranolt_platform as platform
from download import Download
-from xpon.adtran_olt_xpon import AdtranOltXPON
from codec.olt_state import OltState
from flow.flow_entry import FlowEntry
+from resources.adtran_olt_resource_manager import AdtranOltResourceMgr
from net.pio_zmq import PioClient
from net.pon_zmq import PonClient
from voltha.core.flow_decomposer import *
from voltha.extensions.omci.omci import *
from voltha.protos.common_pb2 import AdminState, OperStatus
from voltha.protos.device_pb2 import ImageDownload, Image
-
-ATT_NETWORK = True # Use AT&T cVlan scheme
+from voltha.protos.openflow_13_pb2 import OFPP_MAX
-class AdtranOltHandler(AdtranDeviceHandler, AdtranOltXPON):
+class AdtranOltHandler(AdtranDeviceHandler):
"""
The OLT Handler is used to wrap a single instance of a 10G OLT 1-U pizza-box
"""
@@ -82,22 +80,22 @@
self._downloads = {} # name -> Download obj
self._pio_exception_map = []
+ # 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()
+
def __del__(self):
# OLT Specific things here.
#
# If you receive this during 'enable' of the object, you probably threw an
- # uncaught exception which trigged an errback in the VOLTHA core.
-
+ # uncaught exception which triggered an errback in the VOLTHA core.
d, self.status_poll = self.status_poll, None
- # TODO Any OLT device specific cleanup here
- # def get_channel(self):
- # if self.channel is None:
- # device = self.adapter_agent.get_device(self.device_id)
- # return self.channel
- #
# Clean up base class as well
-
AdtranDeviceHandler.__del__(self)
def _cancel_deferred(self):
@@ -209,10 +207,37 @@
device['software-images'].append(image)
except Exception as e:
- self.log.exception('get-pe-state', e=e)
+ self.log.exception('dev-info-failure', e=e)
+ raise
returnValue(device)
+ def initialize_resource_manager(self):
+ # Initialize the resource manager
+ extra_args = '--olt_vendor {}'.format(self.resource_manager_key)
+ self.resource_mgr = AdtranOltResourceMgr(self.device_id,
+ self.host_and_port,
+ extra_args,
+ self.default_resource_mgr_device_info)
+
+ @property
+ def default_resource_mgr_device_info(self):
+ class AdtranOltDevInfo(object):
+ def __init__(self, pon_ports):
+ self.technology = "gpon"
+ self.onu_id_start = 0
+ self.onu_id_end = platform.MAX_ONUS_PER_PON
+ self.alloc_id_start = platform.MIN_TCONT_ALLOC_ID
+ self.alloc_id_end = platform.MAX_TCONT_ALLOC_ID
+ self.gemport_id_start = platform.MIN_GEM_PORT_ID
+ self.gemport_id_end = platform.MAX_GEM_PORT_ID
+ self.pon_ports = len(pon_ports)
+ self.max_tconts = platform.MAX_TCONTS_PER_ONU
+ self.max_gem_ports = platform.MAX_GEM_PORTS_PER_ONU
+ self.intf_ids = pon_ports.keys() # PON IDs
+
+ return AdtranOltDevInfo(self.southbound_ports)
+
@inlineCallbacks
def enumerate_northbound_ports(self, device):
"""
@@ -342,7 +367,7 @@
port = ports[pon_id + 1]
port['pon-id'] = pon_id
port['admin_state'] = AdminState.ENABLED \
- if data.get('enabled', not self.xpon_support)\
+ if data.get('enabled', True)\
else AdminState.DISABLED
except Exception as e:
@@ -593,8 +618,6 @@
super(AdtranOltHandler, self).delete()
def rx_pa_packet(self, packets):
- self.log.debug('rx-pon-agent-packet')
-
if self._pon_agent is not None:
for packet in packets:
try:
@@ -717,7 +740,7 @@
pkt = Ether(msg)
if self._pio_agent is not None:
- port, ctag, vlan_id, evcmapname = FlowEntry.get_packetout_info(self.device_id, egress_port)
+ port, ctag, vlan_id, evcmapname = FlowEntry.get_packetout_info(self, egress_port)
exceptiontype = None
if pkt.type == FlowEntry.EtherType.EAPOL:
exceptiontype = 'eapol'
@@ -812,20 +835,6 @@
self.status_poll = reactor.callLater(delay, self.poll_for_status)
- def _create_untagged_flow(self):
- nni_port = self.northbound_ports.get(1).port_no
- pon_port = self.southbound_ports.get(0).port_no
-
- return mk_flow_stat(
- priority=100,
- match_fields=[
- in_port(nni_port),
- vlan_vid(ofp.OFPVID_PRESENT + self.untagged_vlan),
- # eth_type(FlowEntry.EtherType.EAPOL) ?? TODO: is this needed
- ],
- actions=[output(pon_port)]
- )
-
def _create_utility_flow(self):
nni_port = self.northbound_ports.get(1).port_no
pon_port = self.southbound_ports.get(0).port_no
@@ -850,7 +859,6 @@
:param device: A voltha.Device object, with possible device-type
specific extensions.
"""
-
self.log.debug('bulk-flow-update', num_flows=len(flows),
device_id=device.id, flows=flows)
@@ -858,21 +866,20 @@
if flows:
# Special helper egress Packet In/Out flows
- for special_flow in (self._create_untagged_flow(),
- self._create_utility_flow()):
- valid_flow, evc = FlowEntry.create(special_flow, self)
+ special_flow = self._create_utility_flow()
+ valid_flow, evc = FlowEntry.create(special_flow, self)
- if valid_flow is not None:
- valid_flows.append(valid_flow.flow_id)
+ if valid_flow is not None:
+ valid_flows.append(valid_flow.flow_id)
- if evc is not None:
- try:
- evc.schedule_install()
- self.add_evc(evc)
+ if evc is not None:
+ try:
+ evc.schedule_install()
+ self.add_evc(evc)
- except Exception as e:
- evc.status = 'EVC Install Exception: {}'.format(e.message)
- self.log.exception('EVC-install', e=e)
+ except Exception as e:
+ evc.status = 'EVC Install Exception: {}'.format(e.message)
+ self.log.exception('EVC-install', e=e)
# verify exception flows were installed by OLT PET process
reactor.callLater(5, self.send_packet_exceptions_request)
@@ -909,7 +916,7 @@
# Now drop all flows from this device that were not in this bulk update
try:
- yield FlowEntry.drop_missing_flows(device.id, valid_flows)
+ yield FlowEntry.drop_missing_flows(self, valid_flows)
except Exception as e:
self.log.exception('bulk-flow-update-remove', e=e)
@@ -941,17 +948,6 @@
else:
self.log.debug('pon-invalid-or-disabled', pon_id=pon_id)
- def get_onu_vid(self, onu_id): # TODO: Deprecate this when packet-in/out is supportted and UNI ports on the OLT
- if ATT_NETWORK:
- return (onu_id * 120) + 2
-
- return None
-
- def get_channel_id(self, pon_id, onu_id): # TODO: Make this more unique. Just don't call the ONU VID method
- from pon_port import PonPort
- return self.get_onu_vid(onu_id)
- # return self._onu_offset(onu_id) + (pon_id * PonPort.MAX_ONUS_SUPPORTED)
-
def _onu_offset(self, onu_id):
# Start ONU's just past the southbound PON port numbers. Since ONU ID's start
# at zero, add one
@@ -982,11 +978,10 @@
return pon_id, onu_id
def _pon_id_to_port_number(self, pon_id):
- # return pon_id + 1 + self.num_northbound_ports
return pon_id + 1 + 4 # Skip over uninitialized ports
def _port_number_to_pon_id(self, port):
- if not self.xpon_support and self.is_uni_port(port):
+ if self.is_uni_port(port):
# Convert to OLT device port
port = platform.intf_id_from_uni_port_num(port)
@@ -996,67 +991,7 @@
return self._port_number_to_pon_id(port) in self.southbound_ports
def is_uni_port(self, port):
- if self.xpon_support:
- return port >= self._onu_offset(0) # TODO: Really need to rework this one...
- else:
- return port >= (5 << 11)
-
- def get_onu_port_and_vlans(self, flow_entry):
- """
- Get the logical port (OpenFlow port) for a given southbound port of an ONU
-
- :param flow_entry: (FlowEntry) Flow to parse
- :return: None or openflow port number and the actual VLAN IDs we should use
- """
- if flow_entry.flow_direction == FlowEntry.FlowDirection.UPSTREAM:
- # Upstream will have VID=Logical_port until VOL-460 is addressed
- ingress_port = flow_entry.in_port
- vid = flow_entry.vlan_id
- else:
- ingress_port = flow_entry.output
- vid = flow_entry.inner_vid
-
- pon_port = self.get_southbound_port(ingress_port)
- if pon_port is None:
- return None, None, None
-
- if flow_entry.flow_direction == FlowEntry.FlowDirection.UPSTREAM:
- self.log.debug('upstream-flow-search', acl=flow_entry.is_acl_flow,
- vid=vid)
- # User data flows have the correct VID / C-Tag.
- if flow_entry.is_acl_flow:
- if self.xpon_support:
- # Upstream ACLs will have VID=Logical_port until VOL-460 is addressed
- onu = next((onu for onu in pon_port.onus if
- onu.logical_port == vid), None)
- else:
- # Upstream ACLs will be placed on the untagged vlan or ONU VID
- # TODO: Do we need an onu_vid set here for DHCP?
- # TODO: For non-xPON, we could really just match the UNI PORT number !!!!
- onu = next((onu for onu in pon_port.onus if
- vid == onu.untagged_vlan), None)
- elif self.xpon_support:
- onu = next((onu for onu in pon_port.onus if
- onu.onu_vid == vid), None)
- else:
- onu = next((onu for onu in pon_port.onus if
- flow_entry.in_port in onu.uni_ports), None)
-
- elif flow_entry.vlan_id in (FlowEntry.LEGACY_CONTROL_VLAN,
- flow_entry.handler.untagged_vlan):
- # User data flows have inner_vid=correct C-tag. Legacy control VLANs
- # have inner_vid == logical_port until VOL-460 is addressed
- onu = next((onu for onu in pon_port.onus if
- onu.logical_port == vid), None)
- else:
- onu = next((onu for onu in pon_port.onus if
- onu.onu_vid == vid), None)
-
- self.log.debug('search-results', onu=onu)
- if onu is None:
- return None, None, None
-
- return onu.logical_port, onu.onu_vid, onu.untagged_vlan
+ return OFPP_MAX >= port >= (5 << 11)
def get_southbound_port(self, port):
pon_id = self._port_number_to_pon_id(port)
@@ -1286,9 +1221,7 @@
self.adapter_agent.update_device(device)
return done
- # SEBA -- New way to launch ONU without xPON support
def add_onu_device(self, intf_id, onu_id, serial_number, tconts, gem_ports):
- assert not self.xpon_support
onu_device = self.adapter_agent.get_child_device(self.device_id,
serial_number=serial_number)
if onu_device is not None:
@@ -1314,14 +1247,12 @@
root=True,
serial_number=serial_number,
admin_state=AdminState.ENABLED,
- # vlan=self.get_onu_vid(onu_id) # TODO: a hack, need a decent flow decomposer
)
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)
- # self._seba_xpon_create(onu_device, intf_id, onu_id, tconts, gem_ports)
reactor.callLater(0, self._seba_xpon_create, onu_device, intf_id, onu_id, tconts, gem_ports)
return onu_device
@@ -1350,370 +1281,27 @@
try:
onu_adapter_agent.create_interface(onu_device,
OnuIndication(intf_id, onu_id))
- except:
+ except Exception as _e:
pass
-
- for tcont in tconts.itervalues():
- 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)
-
- for gem_port in gem_ports.itervalues():
- onu_adapter_agent.create_gemport(onu_device, gem_port.data)
-
- def on_channel_group_modify(self, cgroup, update, diffs):
- valid_keys = ['enable',
- 'polling-period',
- 'system-id'] # 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("channel-group leaf '{}' is read-only or write-once".format(invalid_key))
-
- pons = self.get_related_pons(cgroup)
- keys = [k for k in diffs.keys() if k in valid_keys]
-
- for k in keys:
- if k == 'enabled':
- pass # TODO: ?
-
- elif k == 'polling-period':
- for pon in pons:
- pon.discovery_tick = update[k]
-
- elif k == 'system-id':
- self.system_id(update[k])
-
- return update
-
- def on_channel_partition_modify(self, cpartition, update, diffs):
- valid_keys = ['enabled', 'fec-downstream', 'mcast-aes', 'differential-fiber-distance']
-
- invalid_key = next((key for key in diffs.keys() if key not in valid_keys), None)
- if invalid_key is not None:
- raise KeyError("channel-partition leaf '{}' is read-only or write-once".format(invalid_key))
-
- pons = self.get_related_pons(cpartition)
- keys = [k for k in diffs.keys() if k in valid_keys]
-
- for k in keys:
- if k == 'enabled':
- pass # TODO: ?
-
- elif k == 'fec-downstream':
- for pon in pons:
- pon.downstream_fec_enable = update[k]
-
- elif k == 'mcast-aes':
- for pon in pons:
- pon.mcast_aes = update[k]
-
- elif k == 'differential-fiber-distance':
- for pon in pons:
- pon.deployment_range = update[k] * 1000 # pon-agent uses meters
- return update
-
- def on_channel_pair_modify(self, cpair, update, diffs):
- valid_keys = ['enabled', 'line-rate'] # 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("channel-pair leaf '{}' is read-only or write-once".format(invalid_key))
-
- pons = self.get_related_pons(cpair)
- keys = [k for k in diffs.keys() if k in valid_keys]
-
- for k in keys:
- if k == 'enabled':
- pass # TODO: ?
-
- elif k == 'line-rate':
- for pon in pons:
- pon.line_rate = update[k]
- return update
-
- def on_channel_termination_create(self, ct, pon_type='xgs-ponid'):
- pons = self.get_related_pons(ct, pon_type=pon_type)
- pon_port = pons[0] if len(pons) == 1 else None
-
- if pon_port is None:
- raise ValueError('Unknown PON port. PON-ID: {}'.format(ct[pon_type]))
-
- assert ct['channel-pair'] in self.channel_pairs, \
- '{} is not a channel-pair'.format(ct['channel-pair'])
- cpair = self.channel_pairs[ct['channel-pair']]
-
- assert cpair['channel-group'] in self.channel_groups, \
- '{} is not a -group'.format(cpair['channel-group'])
- assert cpair['channel-partition'] in self.channel_partitions, \
- '{} is not a channel-partition'.format(cpair('channel-partition'))
- cg = self.channel_groups[cpair['channel-group']]
- cpart = self.channel_partitions[cpair['channel-partition']]
-
- polling_period = cg['polling-period']
- system_id = cg['system-id']
- authentication_method = cpart['authentication-method']
- # line_rate = cpair['line-rate']
- downstream_fec = cpart['fec-downstream']
- deployment_range = cpart['differential-fiber-distance']
- mcast_aes = cpart['mcast-aes']
- # TODO: Support BER calculation period
-
- pon_port.xpon_name = ct['name']
- pon_port.discovery_tick = polling_period
- pon_port.authentication_method = authentication_method
- pon_port.deployment_range = deployment_range * 1000 # pon-agent uses meters
- pon_port.downstream_fec_enable = downstream_fec
- pon_port.mcast_aes = mcast_aes
- # pon_port.line_rate = line_rate # TODO: support once 64-bits
- self.system_id = system_id
-
- # Enabled 'should' be a logical 'and' of all referenced items but
- # there is no easy way to detected changes in referenced items.
- # enabled = ct['enabled'] and cpair['enabled'] and cg['enabled'] and cpart['enabled']
- enabled = ct['enabled']
- pon_port.admin_state = AdminState.ENABLED if enabled else AdminState.DISABLED
- return ct
-
- def on_channel_termination_modify(self, ct, update, diffs, pon_type='xgs-ponid'):
- valid_keys = ['enabled'] # 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("channel-termination leaf '{}' is read-only or write-once".format(invalid_key))
-
- pons = self.get_related_pons(ct, pon_type=pon_type)
- pon_port = pons[0] if len(pons) == 1 else None
-
- if pon_port is None:
- raise ValueError('Unknown PON port. PON-ID: {}'.format(ct[pon_type]))
-
- keys = [k for k in diffs.keys() if k in valid_keys]
-
- for k in keys:
- if k == 'enabled':
- enabled = update[k]
- pon_port.admin_state = AdminState.ENABLED if enabled else AdminState.DISABLED
- return update
-
- def on_channel_termination_delete(self, ct, pon_type='xgs-ponid'):
- pons = self.get_related_pons(ct, pon_type=pon_type)
- pon_port = pons[0] if len(pons) == 1 else None
-
- if pon_port is None:
- raise ValueError('Unknown PON port. PON-ID: {}'.format(ct[pon_type]))
-
- pon_port.admin_state = AdminState.DISABLED
- return None
-
- def on_ont_ani_modify(self, ont_ani, update, diffs):
- valid_keys = ['enabled', 'upstream-fec'] # 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("ont-ani leaf '{}' is read-only or write-once".format(invalid_key))
-
- onus = self.get_related_onus(ont_ani)
- keys = [k for k in diffs.keys() if k in valid_keys]
-
- for k in keys:
- if k == 'enabled':
- pass # TODO: Have only ONT use this value?
-
- elif k == 'upstream-fec':
- for onu in onus:
- onu.upstream_fec_enable = update[k]
- return update
-
- def on_vont_ani_modify(self, vont_ani, update, diffs):
- valid_keys = ['enabled',
- 'expected-serial-number',
- 'upstream-channel-speed',
- 'expected-registration-id',
- ] # 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("vont-ani leaf '{}' is read-only or write-once".format(invalid_key))
-
- onus = self.get_related_onus(vont_ani)
- keys = [k for k in diffs.keys() if k in valid_keys]
-
- for k in keys:
- if k == 'enabled':
- for onu in onus:
- onu.enabled = update[k]
- elif k == 'expected-serial-number':
- for onu in onus:
- if onu.serial_number != update[k]:
- onu.pon.delete_onu(onu.onu_id)
- elif k == 'upstream-channel-speed':
- for onu in onus:
- onu.upstream_channel_speed = update[k]
- elif k == 'expected-registration-id':
- for onu in onus:
- onu.password = update[k].encode('base64')
- return update
-
- def on_vont_ani_delete(self, vont_ani):
- onus = self.get_related_onus(vont_ani)
-
- for onu in onus:
- try:
- onu.pon.delete_onu(onu.onu_id)
-
- except Exception as e:
- self.log.exception('onu', onu=onu, e=e)
-
- return None
-
- def _get_tcont_onu(self, vont_ani):
- onu = None
try:
- vont_ani = self.v_ont_anis.get(vont_ani)
- ch_pair = self.channel_pairs.get(vont_ani['preferred-channel-pair'])
- ch_term = next((term for term in self.channel_terminations.itervalues()
- if term['channel-pair'] == ch_pair['name']), None)
+ # 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
- pon = self.pon(ch_term['xgs-ponid'])
- onu = pon.onu(vont_ani['onu-id'])
-
- except Exception:
+ 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
- return onu
-
- def on_tcont_create(self, tcont):
- from xpon.olt_tcont import OltTCont
-
- td = self.traffic_descriptors.get(tcont.get('td-ref'))
- traffic_descriptor = td['object'] if td is not None else None
-
- tcont['object'] = OltTCont.create(tcont, traffic_descriptor)
-
- # Look up any ONU associated with this TCONT (should be only one if any)
- onu = self._get_tcont_onu(tcont['vont-ani'])
-
- if onu is not None: # Has it been discovered yet?
- onu.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
-
- # Look up any ONU associated with this TCONT (should be only one if any)
- onu = self._get_tcont_onu(tcont['vont-ani'])
-
- if onu is not None: # Has it been discovered yet?
- 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:
- onu.update_tcont_td(tcont['alloc-id'], td)
-
- return update
-
- def on_tcont_delete(self, tcont):
- onu = self._get_tcont_onu(tcont['vont-ani'])
-
- if onu is not None:
- onu.remove_tcont(tcont['alloc-id'])
-
- return None
-
- def on_td_create(self, traffic_disc):
- from xpon.olt_traffic_descriptor import OltTrafficDescriptor
- traffic_disc['object'] = OltTrafficDescriptor.create(traffic_disc)
- return traffic_disc
-
- def on_td_modify(self, traffic_disc, update, diffs):
- from xpon.olt_traffic_descriptor import OltTrafficDescriptor
-
- 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'] = OltTrafficDescriptor.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():
- # Look up any ONU associated with this TCONT (should be only one if any)
- onu = self._get_tcont_onu(tcont['vont-ani'])
- if onu is not None:
- onu.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 xpon.olt_gem_port import OltGemPort
- # Create an GemPort object to wrap the dictionary
- gem_port['object'] = OltGemPort.create(self, gem_port)
-
- onus = self.get_related_onus(gem_port)
- assert len(onus) <= 1, 'Too many ONUs: {}'.format(len(onus))
-
- if len(onus) == 1:
- onus[0].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):
- onus = self.get_related_onus(gem_port)
- assert len(onus) <= 1, 'Too many ONUs: {}'.format(len(onus))
- if len(onus) == 1:
- onus[0].remove_gem_id(gem_port['gemport-id'])
- return None
-
class OnuIndication(object):
def __init__(self, intf_id, onu_id):
diff --git a/voltha/adapters/adtran_olt/flow/acl.py b/voltha/adapters/adtran_olt/flow/acl.py
index 42975d8..1845b0f 100644
--- a/voltha/adapters/adtran_olt/flow/acl.py
+++ b/voltha/adapters/adtran_olt/flow/acl.py
@@ -22,8 +22,9 @@
_acl_list = {} # Key -> device-id -> Name: List of encoded EVCs
-ACL_NAME_FORMAT = 'VOLTHA-ACL-{}-{}' # format(flow_entry.handler.device_id, flow_entry.flow.id)
+ACL_NAME_FORMAT = 'VOLTHA-ACL-{}-{}' # format(flow_entry.flow_id, flow-entry-hash)
ACL_NAME_REGEX_ALL = 'VOLTHA-ACL-*'
+ACE_NAME_FORMAT = 'VOLTHA-ACE-{}-{}' # format(flow_entry.flow_id, flow-entry-hash)
class ACL(object):
@@ -194,11 +195,24 @@
@staticmethod
def flow_to_name(flow_entry):
- return 'VOLTHA-ACL-{}-{}'.format(flow_entry.handler.device_id, flow_entry.flow.id)
+ return ACL_NAME_FORMAT.format(flow_entry.flow_id, ACL.acl_hash(flow_entry))
@staticmethod
def flow_to_ace_name(flow_entry):
- return 'VOLTHA-ACE-{}-{}'.format(flow_entry.handler.device_id, flow_entry.flow.id)
+ return ACE_NAME_FORMAT.format(flow_entry.flow_id, ACL.acl_hash(flow_entry))
+
+ @staticmethod
+ def acl_hash(flow_entry):
+ from hashlib import md5
+ in_port = flow_entry.in_port or 0
+ eth_type = flow_entry.eth_type or 0
+ ip_protocol = flow_entry.ip_protocol or 0
+ ipv4_dst = flow_entry.ipv4_dst or 0
+ src_port = flow_entry.udp_src or 0
+ dst_port = flow_entry.udp_dst or 0
+ hex_string = md5('{},{},{},{},{},{}'.format(in_port, eth_type, ip_protocol,
+ ipv4_dst, src_port, dst_port)).hexdigest()
+ return hex_string
@property
def valid(self):
@@ -222,8 +236,8 @@
acls_installed = _acl_list[self._handler.device_id]
if self._name in acls_installed:
- self._status_message = "ACL '{}' id already installed".format(self._name)
- raise Exception(self._status_message)
+ # Return OK
+ returnValue(self._enabled)
try:
acl_xml = self._install_xml()
@@ -334,13 +348,13 @@
pairs = []
if isinstance(entries['acl'], list):
- pairs = { (entry['acl-type'], entry['acl-name']) for entry in entries['acl']
+ pairs = {(entry['acl-type'], entry['acl-name']) for entry in entries['acl']
if 'acl-name' in entry and 'acl-type' in entry and p.match(entry['acl-name'])}
else:
if 'acl' in entries:
entry = entries['acl']
if 'acl-name' in entry and 'acl-type' in entry and p.match(entry['acl-name']):
- pairs = [ (entry['acl-type'], entry['acl-name']) ]
+ pairs = [(entry['acl-type'], entry['acl-name'])]
if len(pairs) > 0:
del_xml = '<access-lists xmlns="http://www.adtran.com/ns/yang/adtran-ietf-access-control-list">'
diff --git a/voltha/adapters/adtran_olt/flow/evc_map.py b/voltha/adapters/adtran_olt/flow/evc_map.py
index 1cafe66..d3e25dc 100644
--- a/voltha/adapters/adtran_olt/flow/evc_map.py
+++ b/voltha/adapters/adtran_olt/flow/evc_map.py
@@ -247,11 +247,12 @@
for onu_or_vlan_id, gem_ids_and_vid in onu_s_gem_ids_and_vid.iteritems():
first_gem_id = True
+ gem_ids = gem_ids_and_vid[0]
vid = gem_ids_and_vid[1]
ident = '{}.{}'.format(self._pon_id, onu_or_vlan_id) if vid is None \
else onu_or_vlan_id
- for gem_id in gem_ids_and_vid[0]:
+ for gem_id in gem_ids:
xml += '<evc-map{}>'.format('' if not create else ' xc:operation="create"')
xml += '<name>{}.{}.{}</name>'.format(self.name, ident, gem_id)
xml += '<ce-vlan-id>{}</ce-vlan-id>'.format(Onu.gem_id_to_gvid(gem_id))
@@ -342,7 +343,7 @@
results = yield self._handler.netconf_client.edit_config(map_xml)
self._installed = results.ok
self._needs_update = results.ok
- self.status = '' if results.ok else results.error
+ self._status_message = '' if results.ok else results.error
if results.ok:
self._existing_acls.update(work_acls)
@@ -502,13 +503,13 @@
"""
from flow_entry import FlowEntry
# Create temporary EVC-MAP
- assert flow.flow_direction == FlowEntry.FlowDirection.UPSTREAM, \
+ assert flow.flow_direction in FlowEntry.upstream_flow_types, \
'Only Upstream flows additions are supported at this time'
log.debug('add-flow-to-evc', flow=flow, evc=evc)
tmp_map = EVCMap.create_ingress_map(flow, evc, dry_run=True) \
- if flow.flow_direction == FlowEntry.FlowDirection.UPSTREAM \
+ if flow.flow_direction in FlowEntry.upstream_flow_types \
else EVCMap.create_egress_map(flow, evc, dry_run=True)
if tmp_map is None or not tmp_map.valid:
@@ -542,7 +543,7 @@
try:
del self._flows[flow.flow_id]
- log('remove-flow-to-evc', flow=flow, evc=evc)
+ log('remove-flow-to-evc', flow=flow)
# Remove any ACLs
acl_name = ACL.flow_to_name(flow)
acl = None
@@ -570,7 +571,7 @@
self._evc.remove_evc_map(self)
first_flow = self._flows.itervalues().next()
- self._evc = first_flow.get_utility_evc(None, True)
+ self._evc = first_flow.get_utility_evc(True)
self._evc.add_evc_map(self)
log.debug('moved-acl-flows-to-utility-evc', newevcname=self._evc.name)
@@ -592,6 +593,8 @@
@staticmethod
def create_evc_map_name(flow):
+ # Note: When actually installed into the OLT, the .onu_id.gem_port is
+ # appended to the name
return EVC_MAP_NAME_FORMAT.format(flow.logical_port, flow.flow_id)
@staticmethod
@@ -605,8 +608,10 @@
"""
items = name.split('-') if name is not None else dict()
+ # Note: When actually installed into the OLT, the .onu_id.gem_port is
+ # appended to the name
return {'ingress-port': items[1],
- 'flow-id': items[2]} if len(items) == 3 else dict()
+ 'flow-id': items[2].split('.')[0]} if len(items) == 3 else dict()
def add_gem_port(self, gem_port, reflow=False):
# TODO: Refactor
@@ -650,8 +655,6 @@
return succeed('nop')
def _setup_gem_ids(self):
- from flow_entry import FlowEntry
-
# all flows should have same GEM port setup
flow = self._flows.itervalues().next()
is_pon = flow.handler.is_pon_port(flow.in_port)
@@ -661,9 +664,8 @@
if pon_port is not None:
self._pon_id = pon_port.pon_id
- untagged_gem = flow.eth_type == FlowEntry.EtherType.EAPOL and\
- flow.handler.untagged_vlan != flow.handler.utility_vlan
- self._gem_ids_and_vid = pon_port.gem_ids(flow.logical_port, untagged_gem,
+ self._gem_ids_and_vid = pon_port.gem_ids(flow.logical_port,
+ flow.vlan_id,
flow.is_multicast_flow)
def _decode(self, evc):
@@ -692,7 +694,6 @@
return False # UNI Ports handled in the EVC Maps
# ACL logic
-
self._eth_type = flow.eth_type
if self._eth_type == FlowEntry.EtherType.IPv4:
@@ -705,7 +706,6 @@
# 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._match_untagged = flow.vlan_id is None and flow.inner_vid is None
diff --git a/voltha/adapters/adtran_olt/flow/flow_entry.py b/voltha/adapters/adtran_olt/flow/flow_entry.py
index fa4fbed..a0d67fe 100644
--- a/voltha/adapters/adtran_olt/flow/flow_entry.py
+++ b/voltha/adapters/adtran_olt/flow/flow_entry.py
@@ -15,7 +15,6 @@
from evc import EVC
from evc_map import EVCMap
from enum import IntEnum
-from untagged_evc import UntaggedEVC
from utility_evc import UtilityEVC
import voltha.core.flow_decomposer as fd
from voltha.core.flow_decomposer import *
@@ -32,18 +31,6 @@
17, # UDP
]
-_existing_downstream_flow_entries = {} # device-id -> signature-table
- # |
- # +-> downstream-signature
- # |
- # +-> 'evc' -> EVC
- # |
- # +-> flow-ids -> flow-entry
-
-_existing_upstream_flow_entries = {} # device-id -> flow dictionary
- # |
- # +-> flow-id -> flow-entry
-
class FlowEntry(object):
"""
@@ -73,9 +60,7 @@
NNI_NNI = 6 # NNI port to NNI Port
UNI_UNI = 7 # UNI port to UNI Port
OTHER = 9 # Unable to determine
- NNI = 10 # Deprecate in v2.0
- UNI = 11 # Deprecate in v2.0
-
+
upstream_flow_types = {FlowDirection.UPSTREAM, FlowDirection.CONTROLLER_UNI}
downstream_flow_types = {FlowDirection.DOWNSTREAM, FlowDirection.NNI_PON}
@@ -122,9 +107,9 @@
# Actions
self.output = None
- self.pop_vlan = 0
- self.push_vlan_tpid = []
- self.push_vlan_id = []
+ self.pop_vlan = False
+ self.push_vlan_tpid = None
+ self.push_vlan_id = None
self._name = self.create_flow_name()
@@ -193,73 +178,75 @@
try:
flow_entry = FlowEntry(flow, handler)
- if not flow_entry._decode():
+ ######################################################################
+ # Decode the flow entry
+ if not flow_entry._decode(flow):
+ # TODO: When we support individual flow mods, we will need to return
+ # this flow back always
return None, None
- if flow_entry.device_id not in _existing_downstream_flow_entries:
- _existing_downstream_flow_entries[flow_entry.device_id] = {}
-
- if flow_entry.device_id not in _existing_upstream_flow_entries:
- _existing_upstream_flow_entries[flow_entry.device_id] = {}
-
- downstream_sig_table = _existing_downstream_flow_entries[flow_entry.device_id]
- upstream_flow_table = _existing_upstream_flow_entries[flow_entry.device_id]
+ ######################################################################
+ # Initialize flow_entry database (dicts) if needed and determine if
+ # the flows have already been handled.
+ downstream_sig_table = handler.downstream_flows
+ upstream_flow_table = handler.upstream_flows
log.debug('flow-entry-decoded', flow=flow_entry, signature=flow_entry.signature,
downstream_signature=flow_entry.downstream_signature)
- if flow_entry.flow_direction == FlowEntry.FlowDirection.UPSTREAM and\
+ if flow_entry.flow_direction in FlowEntry.upstream_flow_types and\
flow_entry.flow_id in upstream_flow_table:
log.debug('flow-entry-upstream-exists', flow=flow_entry)
return flow_entry, None
- if flow_entry.flow_direction == FlowEntry.FlowDirection.DOWNSTREAM and\
- flow_entry.signature in downstream_sig_table and\
- flow_entry.flow_id in downstream_sig_table[flow_entry.signature]:
- log.debug('flow-entry-downstream-exists', flow=flow_entry)
- return flow_entry, None
+ if flow_entry.flow_direction in FlowEntry.downstream_flow_types:
+ sig_table = downstream_sig_table.get(flow_entry.signature)
+ if sig_table is not None and flow_entry in sig_table.flows:
+ log.debug('flow-entry-downstream-exists', flow=flow_entry)
+ return flow_entry, None
- # Look for any matching flows in the other direction that might help make an EVC
- # and then save it off in the device specific flow table
+ ######################################################################
+ # Look for any matching flows in the other direction that might help
+ # make an EVC and then save it off in the device specific flow table
+ #
# TODO: For now, only support for E-LINE services between NNI and UNI
- log.debug('flow-entry-search-for-match', flow=flow_entry)
downstream_flow = None
upstream_flows = None
downstream_sig = None
if flow_entry._is_multicast: # Uni-directional flow
- assert flow_entry._flow_direction == FlowEntry.FlowDirection.DOWNSTREAM, \
+ assert flow_entry._flow_direction in FlowEntry.downstream_flow_types, \
'Only downstream Multicast supported'
downstream_flow = flow_entry
downstream_sig = flow_entry.signature
upstream_flows = []
- elif flow_entry.flow_direction == FlowEntry.FlowDirection.DOWNSTREAM:
+ elif flow_entry.flow_direction in FlowEntry.downstream_flow_types:
downstream_flow = flow_entry
downstream_sig = flow_entry.signature
- elif flow_entry.flow_direction == FlowEntry.FlowDirection.UPSTREAM:
+ elif flow_entry.flow_direction in FlowEntry.upstream_flow_types:
downstream_sig = flow_entry.downstream_signature
if downstream_sig is None:
+ # TODO: When we support individual flow mods, we will need to return
+ # this flow back always
log.debug('flow-entry-empty-downstream', flow=flow_entry)
return None, None
- if downstream_sig not in downstream_sig_table:
- downstream_sig_table[downstream_sig] = {}
- downstream_sig_table[downstream_sig]['evc'] = None
+ # Make sure a slot exists for the downstream signature and get its flow table
+ downstream_sig_table = downstream_sig_table.add(downstream_sig)
+ evc = downstream_sig_table.evc
- downstream_flow_table = downstream_sig_table[downstream_sig]
- evc = downstream_flow_table['evc']
-
- # Save to proper flow table
- if flow_entry.flow_direction == FlowEntry.FlowDirection.UPSTREAM:
- upstream_flow_table[flow_entry.flow_id] = flow_entry
+ # Save the new flow_entry to proper flow table
+ if flow_entry.flow_direction in FlowEntry.upstream_flow_types:
+ upstream_flow_table.add(flow_entry)
downstream_flow = evc.flow_entry if evc is not None else \
- next((_flow for _flow in downstream_flow_table.itervalues() if isinstance(_flow, FlowEntry)), None)
+ next((_flow for _flow in downstream_sig_table.flows.itervalues()
+ if isinstance(_flow, FlowEntry)), None)
- elif flow_entry.flow_direction == FlowEntry.FlowDirection.DOWNSTREAM:
- downstream_flow_table[flow_entry.flow_id] = flow_entry
+ elif flow_entry.flow_direction in FlowEntry.downstream_flow_types:
+ downstream_sig_table.flows.add(flow_entry)
# Now find all the upstream flows
if downstream_flow is not None:
@@ -271,10 +258,13 @@
log.debug('flow-entry-search-results', flow=flow_entry,
downstream_flow=downstream_flow, upstream_flows=upstream_flows)
+ ######################################################################
# Compute EVC and and maps
evc = FlowEntry._create_evc_and_maps(evc, downstream_flow, upstream_flows)
- if evc is not None and evc.valid and downstream_flow_table['evc'] is None:
- downstream_flow_table['evc'] = evc
+
+ # Save off EVC (if we have one) for this flow if it is new
+ if evc is not None and evc.valid and downstream_sig_table.evc is None:
+ downstream_sig_table.evc = evc
return flow_entry, evc
@@ -300,7 +290,6 @@
return None
# Get any existing EVC if a flow is already created
-
if downstream_flow.evc is None:
if evc is not None:
downstream_flow.evc = evc
@@ -310,7 +299,7 @@
downstream_flow.evc = MCastEVC.create(downstream_flow)
elif downstream_flow.is_acl_flow:
- downstream_flow.evc = downstream_flow.get_utility_evc(upstream_flows)
+ downstream_flow.evc = downstream_flow.get_utility_evc()
else:
downstream_flow.evc = EVC(downstream_flow)
@@ -335,11 +324,12 @@
for flow in upstream_flows:
if flow.evc_map is None:
if flow.signature in sig_map_map:
- # Found an explicity matching existing EVC-MAP. Add flow to this EVC-MAP
+ # Found an explicitly matching existing EVC-MAP. Add flow to this EVC-MAP
flow.evc_map = sig_map_map[flow.signature].add_flow(flow, downstream_flow.evc)
else:
# May need to create a MAP or search for an existing ACL/user EVC-Map
- upstream_flow_table = _existing_upstream_flow_entries[flow.device_id]
+ # upstream_flow_table = _existing_upstream_flow_entries[flow.device_id]
+ upstream_flow_table = flow.handler.upstream_flows
existing_flow = EVCMap.find_matching_ingress_flow(flow, upstream_flow_table)
if existing_flow is None:
@@ -356,13 +346,8 @@
return downstream_flow.evc if all_maps_valid else None
- def get_utility_evc(self, upstream_flows=None, use_default_vlan_id=False):
+ def get_utility_evc(self, use_default_vlan_id=False):
assert self.is_acl_flow, 'Utility evcs are for acl flows only'
- if upstream_flows is not None and\
- any(flow.eth_type == FlowEntry.EtherType.EAPOL for flow in upstream_flows) and\
- self.handler.utility_vlan != self.handler.untagged_vlan:
- return UntaggedEVC.create(self, use_default_vlan_id)
-
return UtilityEVC.create(self, use_default_vlan_id)
@property
@@ -373,140 +358,97 @@
return self.eth_type is not None or self.ip_protocol is not None or\
self.ipv4_dst is not None or self.udp_dst is not None or self.udp_src is not None
- def _decode(self):
+ def _decode(self, flow):
"""
Examine flow rules and extract appropriate settings
"""
log.debug('start-decode')
- status = self._decode_traffic_selector() and self._decode_traffic_treatment()
+ status = self._decode_traffic_selector(flow) and self._decode_traffic_treatment(flow)
+ # Determine direction of the flow and apply appropriate modifications
+ # to the decoded flows
if status:
- # Determine direction of the flow
- def port_type(port_number):
- if port_number in self._handler.northbound_ports:
- return FlowEntry.FlowDirection.NNI
- elif port_number <= OFPP_MAX:
- return FlowEntry.FlowDirection.UNI
- return FlowEntry.FlowDirection.OTHER
+ if not self._decode_flow_direction():
+ return False
- self._flow_direction = FlowEntry._flow_dir_map.get((port_type(self.in_port), port_type(self.output)),
- FlowEntry.FlowDirection.OTHER)
+ if self._flow_direction in FlowEntry.downstream_flow_types:
+ status = self._apply_downstream_mods()
- # Modify flow entry for newer utility/untagged VLAN support
- # New Packet In/Out support
- if self._flow_direction == FlowEntry.FlowDirection.DOWNSTREAM and\
- self.vlan_id in (FlowEntry.LEGACY_CONTROL_VLAN,
- self.handler.untagged_vlan):
- # May be for to controller flow downstream (no ethType) or multicast (ethType = IP)
- if self.eth_type is None or self._needs_acl_support:
- self._is_multicast = False
- self._is_acl_flow = True
- if self.inner_vid is not None:
- logical_port, subscriber_vlan, untagged_vlan = \
- self._handler.get_onu_port_and_vlans(self)
- self.inner_vid = subscriber_vlan
- self.vlan_id = self.handler.utility_vlan
- else:
- self.vlan_id = self.handler.untagged_vlan
- elif self._flow_direction == FlowEntry.FlowDirection.UPSTREAM:
- try:
- # TODO: Need to support flow retry if the ONU is not yet activated !!!!
- # Get the correct logical port and subscriber VLAN for this UNI
- self._logical_port, uni_vid, untagged_vlan = \
- self._handler.get_onu_port_and_vlans(self)
+ elif self._flow_direction in FlowEntry.upstream_flow_types:
+ status = self._apply_upstream_mods()
- if self._needs_acl_support:
- self._is_acl_flow = True
- if self.eth_type == FlowEntry.EtherType.EAPOL and \
- self.handler.untagged_vlan != self.handler.utility_vlan:
- self.vlan_id = None
- self.push_vlan_id[0] = self.handler.untagged_vlan
- else:
- self.push_vlan_id[0] = self.handler.utility_vlan
- elif self._handler.xpon_support:
- self.vlan_id = uni_vid
+ else:
+ # TODO: Need to code this - Perhaps this is an NNI_PON for Multicast support?
+ log.error('unsupported-flow-direction')
+ status = False
- except Exception as e:
- # TODO: Need to support flow retry if the ONU is not yet activated !!!!
- log.exception('tag-fixup', e=e)
-
- log.debug('flow-evc-decode', direction=self._flow_direction, is_acl=self._is_acl_flow,
- inner_vid=self.inner_vid, vlan_id=self.vlan_id, pop_vlan=self.pop_vlan,
- push_vids=self.push_vlan_id)
+ log.debug('flow-evc-decode', direction=self._flow_direction, is_acl=self._is_acl_flow,
+ inner_vid=self.inner_vid, vlan_id=self.vlan_id, pop_vlan=self.pop_vlan,
+ push_vid=self.push_vlan_id, status=status)
# Create a signature that will help locate related flow entries on a device.
- # These are not exact, just ones that may be put together to make an EVC. The
- # basic rules are:
- # 1 - Same device
- dev_id = self._handler.device_id
+ if status:
+ # These are not exact, just ones that may be put together to make an EVC. The
+ # basic rules are:
+ #
+ # 1 - Port numbers in increasing order
+ ports = [self.in_port, self.output]
+ ports.sort()
+ assert len(ports) == 2, 'Invalid port count: {}'.format(len(ports))
- # 2 - Port numbers in increasing order
- ports = [self.in_port, self.output]
- ports.sort()
-
- # 3 - The outer VID
- # 4 - The inner VID. Wildcard if downstream
- push_len = len(self.push_vlan_id)
- if push_len == 0:
- outer = self.vlan_id
- inner = self.inner_vid
- else:
- outer = self.push_vlan_id[-1]
- if push_len == 1:
- inner = self.vlan_id
+ # 3 - The outer VID
+ # 4 - The inner VID. Wildcard if downstream
+ if self.push_vlan_id is None:
+ outer = self.vlan_id
+ inner = self.inner_vid
else:
- inner = self.push_vlan_id[-2]
+ outer = self.push_vlan_id
+ inner = self.vlan_id
- upstream_sig = '{}'.format(dev_id)
- downstream_sig = '{}'.format(dev_id)
+ upstream_sig = '{}'.format(ports[0])
+ downstream_sig = '{}'.format(ports[0])
+ upstream_sig += '.{}'.format(ports[1])
+ downstream_sig += '.{}'.format(ports[1] if self.handler.is_nni_port(ports[1]) else '*')
- for port in ports:
- upstream_sig += '.{}'.format(port)
- downstream_sig += '.{}'.format(port if self.handler.is_nni_port(port) else '*')
+ upstream_sig += '.{}.{}'.format(outer, inner)
+ downstream_sig += '.{}.*'.format(outer)
- upstream_sig += '.{}.{}'.format(outer, inner)
- downstream_sig += '.{}.*'.format(outer)
+ if self._flow_direction in FlowEntry.downstream_flow_types:
+ self.signature = downstream_sig
- if self._flow_direction == FlowEntry.FlowDirection.DOWNSTREAM:
- self.signature = downstream_sig
- else:
- self.signature = upstream_sig
- self.downstream_signature = downstream_sig
+ elif self._flow_direction in FlowEntry.upstream_flow_types:
+ self.signature = upstream_sig
+ self.downstream_signature = downstream_sig
- log.debug('flow-evc-decode', upstream_sig=self.signature, downstream_sig=self.downstream_signature)
+ else:
+ log.error('unsupported-flow')
+ status = False
+ log.debug('flow-evc-decode', upstream_sig=self.signature, downstream_sig=self.downstream_signature)
return status
- def _decode_traffic_selector(self):
+ def _decode_traffic_selector(self, flow):
"""
Extract EVC related traffic selection settings
"""
- self.in_port = fd.get_in_port(self._flow)
+ self.in_port = fd.get_in_port(flow)
if self.in_port > OFPP_MAX:
- log.warn('logical-input-ports-not-supported')
+ log.warn('logical-input-ports-not-supported', in_port=self.in_port)
return False
- for field in fd.get_ofb_fields(self._flow):
+ for field in fd.get_ofb_fields(flow):
if field.type == IN_PORT:
- assert self.in_port == field.port, 'Multiple Input Ports found in flow rule'
-
- if self._handler.is_nni_port(self.in_port):
- self._logical_port = self.in_port # TODO: This should be a lookup
+ if self._handler.is_nni_port(self.in_port) or self._handler.is_uni_port(self.in_port):
+ self._logical_port = self.in_port
elif field.type == VLAN_VID:
- # log.info('*** field.type == VLAN_VID', value=field.vlan_vid & 0xfff)
- if self.handler.xpon_support:
- # Traditional xPON way
- self.vlan_id = field.vlan_vid & 0xfff
+ if field.vlan_vid >= ofp.OFPVID_PRESENT + 4095:
+ self.vlan_id = None # pre-ONOS v1.13.5 or old EAPOL Rule
else:
- if field.vlan_vid > ofp.OFPVID_PRESENT + 4095: # Is it a UNI PORT on the PON?
- self.vlan_id = self.handler.untagged_vlan
- else:
- self.vlan_id = field.vlan_vid & 0xfff
+ self.vlan_id = field.vlan_vid & 0xfff
log.debug('*** field.type == VLAN_VID', value=field.vlan_vid, vlan_id=self.vlan_id)
- self._is_multicast = self.vlan_id in self._handler.multicast_vlans
elif field.type == VLAN_PCP:
log.debug('*** field.type == VLAN_PCP', value=field.vlan_pcp)
@@ -521,7 +463,7 @@
self.ip_protocol = field.ip_proto
if self.ip_protocol not in _supported_ip_protocols:
- # log.error('Unsupported IP Protocol')
+ log.error('Unsupported IP Protocol', protocol=self.ip_protocol)
return False
elif field.type == IPV4_DST:
@@ -538,17 +480,21 @@
elif field.type == METADATA:
log.debug('*** field.type == METADATA', value=field.table_metadata)
- if self.handler.xpon_support:
- # Traditional xPON way
- self.inner_vid = field.table_metadata
+
+ if 0xFFFFFFFF >= field.table_metadata > ofp.OFPVID_PRESENT + 4095:
+ # Default flows for old-style controller flows
+ self.inner_vid = None
+
+ elif field.table_metadata > 0xFFFFFFFF:
+ # ONOS v1.13.5 or later. c-vid in upper 32-bits
+ self.inner_vid = field.table_metadata >> 32
+
else:
- if field.table_metadata > ofp.OFPVID_PRESENT + 4095: # Is it a UNI PORT on the PON?
- self.inner_vid = self.handler.untagged_vlan
- else:
- self.inner_vid = field.table_metadata
+ # Pre- ONOS v1.13.5
+ self.inner_vid = field.table_metadata
- log.debug('*** field.type == METADATA', value=field.table_metadata, inner_vid=self.inner_vid)
-
+ 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)
@@ -556,61 +502,149 @@
return True
- def _decode_traffic_treatment(self):
- self.output = fd.get_out_port(self._flow)
-
- if self.output > OFPP_MAX:
- log.warn('logical-output-ports-not-supported')
- return False
-
- for act in fd.get_actions(self._flow):
+ def _decode_traffic_treatment(self, flow):
+ # Loop through traffic treatment
+ for act in fd.get_actions(flow):
if act.type == fd.OUTPUT:
- assert self.output == act.output.port, 'Multiple Output Ports found in flow rule'
- pass # Handled earlier
+ self.output = act.output.port
elif act.type == POP_VLAN:
log.debug('*** action.type == POP_VLAN')
- self.pop_vlan += 1
+ self.pop_vlan = True
elif act.type == PUSH_VLAN:
log.debug('*** action.type == PUSH_VLAN', value=act.push)
- # TODO: Do we want to test the ethertype for support?
tpid = act.push.ethertype
- self.push_vlan_tpid.append(tpid)
+ self.push_vlan_tpid = tpid
elif act.type == SET_FIELD:
log.debug('*** action.type == SET_FIELD', value=act.set_field.field)
assert (act.set_field.field.oxm_class == ofp.OFPXMC_OPENFLOW_BASIC)
field = act.set_field.field.ofb_field
- if field.type == VLAN_VID:
- self.push_vlan_id.append(field.vlan_vid & 0xfff)
+ if field.type == VLAN_VID:
+ self.push_vlan_id = field.vlan_vid & 0xfff
+ else:
+ log.debug('unsupported-set-field')
else:
- # TODO: May need to modify ce-preservation
log.warn('unsupported-action', action=act)
self._status_message = 'Unsupported action.type={}'.format(act.type)
return False
return True
- @staticmethod
- def drop_missing_flows(device_id, valid_flow_ids):
- dl = []
- try:
- flow_table = _existing_upstream_flow_entries.get(device_id)
- if flow_table is not None:
- flows_to_drop = [flow for flow_id, flow in flow_table.items()
- if flow_id not in valid_flow_ids]
- dl.extend([flow.remove() for flow in flows_to_drop])
+ def _decode_flow_direction(self):
+ # Determine direction of the flow
+ def port_type(port_number):
+ if port_number in self._handler.northbound_ports:
+ return FlowEntry.PortType.NNI
- sig_table = _existing_downstream_flow_entries.get(device_id)
- if sig_table is not None:
- for flow_table in sig_table.itervalues():
- flows_to_drop = [flow for flow_id, flow in flow_table.items()
- if isinstance(flow, FlowEntry) and flow_id not in valid_flow_ids]
- dl.extend([flow.remove() for flow in flows_to_drop])
+ elif port_number in self._handler.southbound_ports:
+ return FlowEntry.PortType.PON
+
+ elif port_number <= OFPP_MAX:
+ return FlowEntry.PortType.UNI
+
+ elif port_number in {OFPP_CONTROLLER, 0xFFFFFFFD}: # OFPP_CONTROLLER is wrong in proto-file
+ return FlowEntry.PortType.CONTROLLER
+
+ return FlowEntry.PortType.OTHER
+
+ flow_dir_map = {
+ (FlowEntry.PortType.UNI, FlowEntry.PortType.NNI): FlowEntry.FlowDirection.UPSTREAM,
+ (FlowEntry.PortType.NNI, FlowEntry.PortType.UNI): FlowEntry.FlowDirection.DOWNSTREAM,
+ (FlowEntry.PortType.UNI, FlowEntry.PortType.CONTROLLER): FlowEntry.FlowDirection.CONTROLLER_UNI,
+ (FlowEntry.PortType.NNI, FlowEntry.PortType.PON): FlowEntry.FlowDirection.NNI_PON,
+ # The following are not yet supported
+ # (FlowEntry.PortType.NNI, FlowEntry.PortType.CONTROLLER): FlowEntry.FlowDirection.CONTROLLER_NNI,
+ # (FlowEntry.PortType.PON, FlowEntry.PortType.CONTROLLER): FlowEntry.FlowDirection.CONTROLLER_PON,
+ # (FlowEntry.PortType.NNI, FlowEntry.PortType.NNI): FlowEntry.FlowDirection.NNI_NNI,
+ # (FlowEntry.PortType.UNI, FlowEntry.PortType.UNI): FlowEntry.FlowDirection.UNI_UNI,
+ }
+ self._flow_direction = flow_dir_map.get((port_type(self.in_port), port_type(self.output)),
+ FlowEntry.FlowDirection.OTHER)
+ return self._flow_direction != FlowEntry.FlowDirection.OTHER
+
+ def _apply_downstream_mods(self):
+ # This is a downstream flow. It could be any one of the following:
+ #
+ # Legacy control VLAN:
+ # This is the old VLAN 4000 that was used to attach EAPOL and other
+ # controller flows to. Eventually these will change to CONTROLLER_UNI
+ # flows. For these, use the 'utility' VLAN instead so 4000 if available
+ # for other uses (AT&T uses it for downstream multicast video).
+ #
+ # Multicast VLAN:
+ # This is downstream multicast data.
+ # TODO: Test this to see if this needs to be in a separate NNI_PON mod-method
+ #
+ # User Data flow:
+ # This is for user data. Eventually we may need to support ACLs?
+ #
+ # May be for to controller flow downstream (no ethType)
+ if self.vlan_id == FlowEntry.LEGACY_CONTROL_VLAN and self.eth_type is None and self.pcp == 0:
+ return False # Do not install this flow. Utility VLAN is in charge
+
+ elif self.vlan_id in self._handler.multicast_vlans:
+ # multicast (ethType = IP) # TODO: May need to be an NNI_PON flow
+ self._is_multicast = True
+ self._is_acl_flow = True
+
+ else:
+ # Currently do not support ACLs on user data flows downstream
+ assert not self._needs_acl_support # User data, no special modifications needed at this time
+
+ return True
+
+ def _apply_upstream_mods(self):
+ #
+ # This is an upstream flow. It could be any of the following
+ #
+ # ACL/Packet capture:
+ # This is either a legacy (FlowDirection.UPSTREAM) or a new one
+ # that specifies an output port of controller (FlowDirection.CONTROLLER_UNI).
+ # Either way, these need to be placed on the Utility VLAN if the ONU attached
+ # does not have a user-data flow (C-Tag). If there is a C-Tag available,
+ # then place it on that VLAN.
+ #
+ # Once a user-data flow is established, move any of the ONUs ACL flows
+ # over to that VLAN (this is handled elsewhere).
+ #
+ # User Data flows:
+ # No special modifications are needed
+ #
+ try:
+ # Do not handle PON level ACLs in this method
+ assert(self._flow_direction != FlowEntry.FlowDirection.CONTROLLER_PON)
+
+ # Is this a legacy (VLAN 4000) upstream to-controller flow
+ if self._needs_acl_support and FlowEntry.LEGACY_CONTROL_VLAN == self.push_vlan_id:
+ self._flow_direction = FlowEntry.FlowDirection.CONTROLLER_UNI
+ self._is_acl_flow = True
+ self.push_vlan_id = self.handler.utility_vlan
+
+ return True
except Exception as e:
+ # TODO: Need to support flow retry if the ONU is not yet activated !!!!
+ log.exception('tag-fixup', e=e)
+ return False
+
+ @staticmethod
+ def drop_missing_flows(handler, valid_flow_ids):
+ dl = []
+ try:
+ flow_table = handler.upstream_flows
+ flows_to_drop = [flow for flow_id, flow in flow_table.items()
+ if flow_id not in valid_flow_ids]
+ dl.extend([flow.remove() for flow in flows_to_drop])
+
+ for sig_table in handler.downstream_flows.itervalues():
+ flows_to_drop = [flow for flow_id, flow in sig_table.flows.items()
+ if isinstance(flow, FlowEntry) and flow_id not in valid_flow_ids]
+ dl.extend([flow.remove() for flow in flows_to_drop])
+
+ except Exception as _e:
pass
return gatherResults(dl, consumeErrors=True) if len(dl) > 0 else returnValue('no-flows-to-drop')
@@ -622,37 +656,30 @@
if needed
"""
# Remove from exiting table list
-
- device_id = self._handler.device_id
- flow_id = self._flow.id
+ flow_id = self.flow_id
flow_table = None
- sig_table = None
- if self.flow_direction == FlowEntry.FlowDirection.UPSTREAM:
- flow_table = _existing_upstream_flow_entries.get(device_id)
+ if self.flow_direction in FlowEntry.upstream_flow_types:
+ flow_table = self._handler.upstream_flows
- elif self.flow_direction == FlowEntry.FlowDirection.DOWNSTREAM:
- sig_table = _existing_downstream_flow_entries.get(device_id)
- flow_table = sig_table.get(self.signature)
+ elif self.flow_direction in FlowEntry.downstream_flow_types:
+ 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:
returnValue('NOP')
# Remove from flow table and clean up flow table if empty
-
- del flow_table[flow_id]
+ flow_table.remove(flow_id)
evc_map, self.evc_map = self.evc_map, None
evc = None
- if self.flow_direction == FlowEntry.FlowDirection.UPSTREAM:
- if len(flow_table) == 0:
- del _existing_upstream_flow_entries[device_id]
-
- elif self.flow_direction == FlowEntry.FlowDirection.DOWNSTREAM:
- if len(flow_table) == 1: # Only 'evc' entry present
- evc = flow_table['evc']
+ if self.flow_direction in FlowEntry.downstream_flow_types:
+ sig_table = self._handler.downstream_flows.get(self.signature)
+ if len(flow_table) == 0: # Only 'evc' entry present
+ evc = sig_table.evc
else:
- assert flow_table['evc'] is not None, 'EVC flow re-assignment error'
+ assert sig_table.evc is not None, 'EVC flow re-assignment error'
# Remove flow from the hardware
try:
@@ -668,23 +695,20 @@
except Exception as e:
log.exception('removal', e=e)
- if self.flow_direction == FlowEntry.FlowDirection.DOWNSTREAM:
-
+ if self.flow_direction in FlowEntry.downstream_flow_types:
# If this flow owns the EVC, assign it to a remaining flow
- flow_evc = flow_table['evc']
+ sig_table = self._handler.downstream_flows.get(self.signature)
+ flow_evc = sig_table.evc
- if flow_evc is not None and flow_evc.flow_entry is not None and \
- flow_id == flow_evc.flow_entry.flow_id:
- flow_table['evc'].flow_entry = next((_flow for _flow in flow_table.itervalues()
- if isinstance(_flow, FlowEntry)
- and _flow.flow_id != flow_id), None)
+ if flow_evc is not None and flow_evc.flow_entry is not None and flow_id == flow_evc.flow_entry.flow_id:
+ flow_evc.flow_entry = next((_flow for _flow in flow_table.itervalues()
+ if isinstance(_flow, FlowEntry)
+ and _flow.flow_id != flow_id), None)
- # If evc was deleted, remove the flow table reference
+ # If evc was deleted, remove the signature table since now flows exist with
+ # that signature
if evc is not None:
- del flow_table['evc']
- del sig_table[self.signature]
- if len(sig_table) == 0:
- del _existing_downstream_flow_entries[device_id]
+ self._handler.downstream_flows.remove(self.signature)
self.evc = None
returnValue('Done')
@@ -696,12 +720,10 @@
:param onu: (Onu) onu
:return: (list) of matching flows
"""
- # EVCs are only in the downstream table, EVC Map are in upstream
-
- device_id = onu.device_id
+ # EVCs are only in the downstream table, EVC Maps are in upstream
onu_ports = onu.uni_ports
- all_flow_entries = _existing_upstream_flow_entries.get(device_id) or {}
+ all_flow_entries = onu.olt.upstream_flows
evc_maps = [flow_entry.evc_map for flow_entry in all_flow_entries.itervalues()
if flow_entry.in_port in onu_ports
and flow_entry.evc_map is not None
@@ -740,33 +762,28 @@
# Bulk operations
@staticmethod
- def clear_all(device_id):
+ def clear_all(handler):
"""
Remove all flows for the device.
- :param device_id: voltha adapter device id
+ :param handler: voltha adapter device handler
"""
-
- if device_id in _existing_downstream_flow_entries:
- del _existing_downstream_flow_entries[device_id]
-
- if device_id in _existing_upstream_flow_entries:
- del _existing_upstream_flow_entries[device_id]
+ handler.downstream_flows.clear_all()
+ handler.upstream_flows.clear_all()
@staticmethod
- def get_packetout_info(device_id, logical_port):
+ def get_packetout_info(handler, logical_port):
"""
- Find parameters needed to send packet out succesfully to the OLT.
+ Find parameters needed to send packet out successfully to the OLT.
- :param device_id: A Voltha.Device object.
+ :param handler: voltha adapter device handler
:param logical_port: (int) logical port number for packet to go out.
:return: physical port number, ctag, stag, evcmap name
"""
from ..onu import Onu
- all_flow_entries = _existing_upstream_flow_entries.get(device_id) or {}
- for flow_entry in all_flow_entries.itervalues():
+ for flow_entry in handler.upstream_flows.itervalues():
log.debug('get-packetout-info', flow_entry=flow_entry)
# match logical port
@@ -785,5 +802,5 @@
ctag = gem_ids_with_vid[1]
gem_id = gem_ids[0] # TODO: always grab fist in list
return flow_entry.in_port, ctag, Onu.gem_id_to_gvid(gem_id), \
- evc_map.get_evcmap_name(onu_id, gem_id)
+ evc_map.get_evcmap_name(onu_id, gem_id)
return None, None, None, None
diff --git a/voltha/adapters/adtran_olt/flow/flow_tables.py b/voltha/adapters/adtran_olt/flow/flow_tables.py
new file mode 100644
index 0000000..e90e69b
--- /dev/null
+++ b/voltha/adapters/adtran_olt/flow/flow_tables.py
@@ -0,0 +1,162 @@
+# 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.
+#
+from flow_entry import FlowEntry
+from evc import EVC
+
+
+class DeviceFlows(object):
+ """ Tracks existing flows on the device """
+
+ def __init__(self):
+ self._flow_table = dict() # Key = (str)Flow ID, Value = FlowEntry
+
+ def __getitem__(self, item):
+ flow_id = item.flow_id if isinstance(item, FlowEntry) else item
+ return self._flow_table[flow_id]
+
+ def __iter__(self):
+ for _flow_id, _flow in self._flow_table.items():
+ yield _flow_id, _flow
+
+ def itervalues(self):
+ for _flow in self._flow_table.values():
+ yield _flow
+
+ def iterkeys(self):
+ for _id in self._flow_table.keys():
+ yield _id
+
+ def items(self):
+ return self._flow_table.items()
+
+ def values(self):
+ return self._flow_table.values()
+
+ def keys(self):
+ return self._flow_table.keys()
+
+ def __len__(self):
+ return len(self._flow_table)
+
+ def add(self, flow):
+ assert isinstance(flow, FlowEntry)
+ self._flow_table[flow.flow_id] = flow
+ return flow
+
+ def get(self, item):
+ flow_id = item.flow_id if isinstance(item, FlowEntry) else item
+ return self._flow_table.get(flow_id)
+
+ def remove(self, item):
+ flow_id = item.flow_id if isinstance(item, FlowEntry) else item
+ return self._flow_table.pop(flow_id, default=None)
+
+ def clear_all(self):
+ self._flow_table = dict()
+
+
+class DownstreamFlows(object):
+ """
+ Tracks existing flows that are downstream (NNI as source port)
+
+ The downstream table is slightly different than the base DeviceFlows
+ table as it is used to track flows that will become EVCs. The base
+ table tracks flows that will be EVC-maps (or related to them).
+
+ The downstream table is also indexed by a downstream signature that
+ is composed as follows:
+
+ <dev-id>.<ingress-port-number>.<s-tag>.*
+
+ In comparison, the upstream flows is similar, but instead of '*' it has the
+ c-tag (if any).
+
+ TODO: Drop device ID from signatures once flow tables are unique to a device handler
+ """
+ def __init__(self):
+ self._signature_table = dict() # Key = (str)Downstream signature
+ # |
+ # +-> downstream-signature
+ # |
+ # +-> 'evc' -> EVC
+ # |
+ # +-> flow-ids -> flow-entries...
+
+ def __getitem__(self, signature):
+ assert isinstance(signature, str)
+ return self._signature_table[signature]
+
+ def __iter__(self):
+ for _flow_id, _flow in self._signature_table.items():
+ yield _flow_id, _flow
+
+ def itervalues(self):
+ for _flow in self._signature_table.values():
+ yield _flow
+
+ def iterkeys(self):
+ for _id in self._signature_table.keys():
+ yield _id
+
+ def items(self):
+ return self._signature_table.items()
+
+ def values(self):
+ return self._signature_table.values()
+
+ def keys(self):
+ return self._signature_table.keys()
+
+ def __len__(self):
+ return len(self._signature_table)
+
+ def get(self, signature):
+ assert isinstance(signature, str)
+ return self._signature_table.get(signature)
+
+ def add(self, signature):
+ assert isinstance(signature, str)
+ """
+ Can be called by upstream flow to reserve a slot
+ """
+ if signature not in self._signature_table:
+ self._signature_table[signature] = DownstreamFlows.SignatureTableEntry(signature)
+ return self._signature_table[signature]
+
+ def remove(self, signature):
+ assert isinstance(signature, str)
+ return self._signature_table.pop(signature)
+
+ def clear_all(self):
+ self._signature_table = dict()
+
+ class SignatureTableEntry(object):
+ def __init__(self, signature):
+ self._signature = signature
+ self._evc = None
+ self._flow_table = DeviceFlows()
+
+ @property
+ def evc(self):
+ return self._evc
+
+ @evc.setter
+ def evc(self, evc):
+ assert isinstance(evc, (EVC, type(None)))
+ self._evc = evc
+
+ @property
+ def flows(self):
+ return self._flow_table
diff --git a/voltha/adapters/adtran_olt/flow/mcast.py b/voltha/adapters/adtran_olt/flow/mcast.py
index 124a262..164d0f4 100644
--- a/voltha/adapters/adtran_olt/flow/mcast.py
+++ b/voltha/adapters/adtran_olt/flow/mcast.py
@@ -173,11 +173,11 @@
self.in_port, self.output = self.output, self.in_port
self.flow_id = '{}-MCAST'.format(self.vlan_id)
self._logical_port = self.vlan_id
- self.push_vlan_id = [self.vlan_id]
+ self.push_vlan_id = self.vlan_id
self.vlan_id = None
self.signature = None
self.inner_vid = None
- self.pop_vlan = 0
+ self.pop_vlan = False
def create_flow_name(self):
return 'flow-{}-{}-MCAST'.format(self.device_id, self.vlan_id)
diff --git a/voltha/adapters/adtran_olt/flow/untagged_evc.py b/voltha/adapters/adtran_olt/flow/untagged_evc.py
deleted file mode 100644
index c51459f..0000000
--- a/voltha/adapters/adtran_olt/flow/untagged_evc.py
+++ /dev/null
@@ -1,158 +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.
-
-from evc import EVC
-from twisted.internet import defer
-from twisted.internet.defer import returnValue, inlineCallbacks
-import voltha.core.flow_decomposer as fd
-from voltha.core.flow_decomposer import *
-
-log = structlog.get_logger()
-
-EVC_NAME_FORMAT = 'VOLTHA-UNTAGGED-{}' # format(flow.vlan_id)
-EVC_NAME_REGEX_ALL = EVC_NAME_FORMAT.format('*')
-
-
-_untagged_evcs = {} # device-id -> flow dictionary
- # |
- # +-> untagged-vlan-id -> evcs
-
-
-class UntaggedEVC(EVC):
- """
- Class to wrap Untagged (no C-Tag) EVC functionality
- """
- def __init__(self, flow_entry):
- super(UntaggedEVC, self).__init__(flow_entry)
- # No Inner-VID
- self._switching_method = EVC.SwitchingMethod.SINGLE_TAGGED
- self._downstream_flows = {flow_entry.flow_id} # Matching Downstream Flow IDs
- self.service_evc = True
-
- def __str__(self):
- return "VOLTHA-UNTAGGED-{}: MEN: {}, VLAN: {}".format(self._name, self._men_ports, self._s_tag)
-
- def _create_name(self, vlan_id=None):
- #
- # TODO: Take into account selection criteria and output to make the name
- #
- return EVC_NAME_FORMAT.format(self._flow.vlan_id if vlan_id is None else vlan_id)
-
- @staticmethod
- def create(flow_entry, use_default_vlan_id=False):
- device_id = flow_entry.device_id
- vlan_id = flow_entry.vlan_id if use_default_vlan_id else flow_entry.handler.untagged_vlan
- evc_table = _untagged_evcs.get(device_id)
-
- if evc_table is None:
- _untagged_evcs[device_id] = dict()
- evc_table = _untagged_evcs[device_id]
-
- try:
- evc = evc_table.get(flow_entry.vlan_id)
-
- if evc is None:
- # Create EVC and initial EVC Map
- evc = UntaggedEVC(flow_entry)
-
- # reapply the stag and name if forced vlan id
- if use_default_vlan_id:
- evc._s_tag = vlan_id
- evc._create_name(vlan_id)
-
- evc_table[flow_entry.vlan_id] = evc
- else:
- if flow_entry.flow_id in evc.downstream_flows: # TODO: Debug only to see if flow_ids are unique
- pass
- else:
- evc.add_downstream_flows(flow_entry.flow_id)
-
- return evc
-
- except Exception as e:
- log.exception('untagged-create', e=e)
- return None
-
- @property
- def downstream_flows(self):
- return frozenset(self._downstream_flows)
-
- def add_downstream_flows(self, flow_id):
- self._downstream_flows.add(flow_id)
-
- def remove_downstream_flows(self, flow_id):
- self._downstream_flows.discard(flow_id)
-
- def remove(self, remove_maps=True):
- """
- Remove EVC (and optional associated EVC-MAPs) from hardware
- :param remove_maps: (boolean)
- :return: (deferred)
- """
- log.info('removing', evc=self, remove_maps=remove_maps)
-
- device_id = self._flow.handler.device_id
- flow_id = self._flow.flow_id
- evc_table = _untagged_evcs.get(device_id)
-
- if evc_table is None:
- return defer.succeed('NOP')
-
- # Remove flow reference
- if self._flow.flow_id in self._downstream_flows:
- self._downstream_flows.discard(self._flow.flow_id)
-
- if len(self._downstream_flows) == 0:
- # Use base class to clean up
- return super(UntaggedEVC, self).remove(remove_maps=True)
-
- return defer.succeed('More references')
-
- @inlineCallbacks
- def delete(self, delete_maps=True):
- """
- Remove from hardware and delete/clean-up EVC Object
- """
- log.info('deleting', evc=self, delete_maps=delete_maps)
-
- assert self._flow, 'Delete EVC must have flow reference'
- try:
- dl = [self.remove()]
- if delete_maps:
- for evc_map in self.evc_maps:
- dl.append(evc_map.delete(None)) # TODO: implement bulk-flow procedures
-
- yield defer.gatherResults(dl, consumeErrors=True)
-
- self._evc_maps = None
- f, self._flow = self._flow, None
- if f is not None and f.handler is not None:
- f.handler.remove_evc(self)
-
- except Exception as e:
- log.exception('removal', e=e)
-
- def reflow(self, reflow_maps=True):
- pass # TODO: Implement or use base class?
-
- @staticmethod
- def remove_all(client, regex_=EVC_NAME_REGEX_ALL):
- """
- Remove all matching EVCs from hardware
- :param client: (ncclient) NETCONF Client to use
- :param regex_: (String) Regular expression for name matching
- :return: (deferred)
- """
- _untagged_evcs.clear()
- EVC.remove_all(client, regex_)
diff --git a/voltha/adapters/adtran_olt/onu.py b/voltha/adapters/adtran_olt/onu.py
index 362688a..41e8b0f 100644
--- a/voltha/adapters/adtran_olt/onu.py
+++ b/voltha/adapters/adtran_olt/onu.py
@@ -31,8 +31,6 @@
"""
Wraps an ONU
"""
- MIN_ONU_ID = 0
- MAX_ONU_ID = 1020 # 1021..2013 reserved
DEFAULT_PASSWORD = ''
def __init__(self, onu_info):
@@ -41,98 +39,47 @@
raise ValueError('No ONU ID available')
pon = onu_info['pon']
+ self._olt = pon.olt
+ self._pon_id = pon.pon_id
+ self._name = '{}@{}'.format(pon.physical_port_name, self._onu_id)
+ self.log = structlog.get_logger(pon_id=self._pon_id, onu_id=self._onu_id)
+
+ self._valid = True # Set false during delete/cleanup
self._serial_number_base64 = Onu.string_to_serial_number(onu_info['serial-number'])
self._serial_number_string = onu_info['serial-number']
self._device_id = onu_info['device-id']
self._password = onu_info['password']
- self._olt = pon.olt
- self._pon_id = pon.pon_id
- self._name = '{}@{}'.format(pon.name, self._onu_id)
- self._xpon_name = onu_info['xpon-name']
- self._gem_ports = {} # gem-id -> GemPort
- self._tconts = {} # alloc-id -> TCont
- if self.olt.xpon_support:
- self._onu_vid = onu_info['onu-vid'] # SEBA (xpon-mode may be only one using this)
- self.untagged_vlan = self._onu_vid
- self._uni_ports = [onu_info['onu-vid']] # TODO: Get rid of this # SEBA (xpon-mode may be only one using this)
- assert len(self._uni_ports) == 1, 'Only one UNI port supported at this time'
- else:
- self._onu_vid = None
- self.untagged_vlan = self.olt.untagged_vlan # SEBA - BBWF has this hard coded to 4091
- self._uni_ports = onu_info['uni-ports']
- self._channel_id = onu_info['channel-id'] # SEBA (xpon-mode may be only one using this)
- self._enabled = onu_info['enabled']
- self._vont_ani = onu_info.get('vont-ani')
- self._rssi = -9999
- self._equalization_delay = 0
- self._fiber_length = 0
- self._valid = True # Set false during delete/cleanup
+
self._created = False
self._proxy_address = None
- self._upstream_fec_enable = onu_info.get('upstream-fec')
- self._upstream_channel_speed = onu_info['upstream-channel-speed']
- # TODO: how do we want to enforce upstream channel speed (if at all)?
- self._include_multicast = True # TODO: May need to add multicast on a per-ONU basis
self._sync_tick = _HW_SYNC_SECS
self._expedite_sync = False
self._expedite_count = 0
self._resync_flows = False
self._sync_deferred = None # For sync of ONT config to hardware
- self._password = None
- self._timestamp = None
- if onu_info['venet'] is not None:
- port_no, subscriber_vlan, self.untagged_vlan = Onu.decode_venet(onu_info['venet'],
- self.olt.untagged_vlan)
- if port_no is not None:
- self._uni_ports = [port_no]
- if subscriber_vlan is not None:
- self._onu_vid = subscriber_vlan
+ # Resources
+ self._gem_ports = {} # gem-id -> GemPort
+ self._tconts = {} # alloc-id -> TCont
+ self._uni_ports = onu_info['uni-ports']
- self.log = structlog.get_logger(pon_id=self._pon_id, onu_id=self._onu_id)
+ # Provisionable items
+ self._enabled = onu_info['enabled']
+ self._upstream_fec_enable = onu_info.get('upstream-fec')
+ self._upstream_channel_speed = onu_info['upstream-channel-speed']
+ # TODO: how do we want to enforce upstream channel speed (if at all)?
- def __del__(self):
- # self.stop()
- pass
+ # KPI related items
+ self._rssi = -9999
+ self._equalization_delay = 0
+ self._fiber_length = 0
+ self._timestamp = None # Last time the KPI items were updated
def __str__(self):
- return "ONU-{}:{}, SN: {}/{}".format(self._onu_id, self._pon_id,
+ return "ONU-{}:{}, SN: {}/{}".format(self._pon_id, self._onu_id,
self._serial_number_string, self._serial_number_base64)
@staticmethod
- def decode_venet(venet_info, untagged_vlan):
- # TODO: Move this one and ONU one into venet decode to dict() area
- try:
- # Allow spaces or dashes as separator, select last as the
- # port number. UNI-1, UNI 1, and UNI 3-2-1 are the same
- port_no = int(venet_info['name'].replace(' ', '-').split('-')[-1:][0])
- subscriber_vlan = port_no
- try:
- # Subscriber VLAN and Untagged vlan are comma separated
- parts = venet_info['description'].split(',')
- sub_part = next((part for part in parts if 'vlan' in part.lower()), None)
- untagged_part = next((part for part in parts if 'untagged' in part.lower()), None)
- try:
- if sub_part is not None:
- subscriber_vlan = int(sub_part.split(':')[-1:][0])
- except Exception as e:
- pass
- try:
- if untagged_part is not None:
- untagged_vlan = int(untagged_part.split(':')[-1:][0])
- except Exception as e:
- pass
- except Exception as e:
- pass
-
- return port_no, subscriber_vlan, untagged_vlan
-
- except ValueError:
- pass
- except KeyError:
- pass
-
- @staticmethod
def serial_number_to_string(value):
sval = base64.decodestring(value)
unique = [elem.encode("hex") for elem in sval[4:8]]
@@ -174,14 +121,6 @@
return self._name
@property
- def xpon_name(self):
- return self._xpon_name
-
- @property
- def v_ont_ani(self):
- return self._vont_ani
-
- @property
def upstream_fec_enable(self):
return self._upstream_fec_enable
@@ -200,7 +139,7 @@
@upstream_channel_speed.setter
def upstream_channel_speed(self, value):
- assert isinstance(value, (int,float)), 'upstream speed is a numeric value'
+ assert isinstance(value, (int, float)), 'upstream speed is a numeric value'
if self._upstream_channel_speed != value:
self._upstream_channel_speed = value
@@ -219,10 +158,9 @@
"""
if self._password is None and value is not None:
self._password = value
- # reg_id = value.decode('base64')
reg_id = (value.decode('base64')).rstrip('\00').lstrip('\00')
# Must remove any non-printable characters
- reg_id = ''.join([i if ord(i) < 127 and ord(i) > 31 else '_' for i in reg_id])
+ reg_id = ''.join([i if 127 > ord(i) > 31 else '_' for i in reg_id])
# Generate alarm here for regID
from voltha.extensions.alarms.onu.onu_active_alarm import OnuActiveAlarm
self.log.info('onu-Active-Alarm', serial_number=self._serial_number_string)
@@ -253,10 +191,6 @@
self.pon.upstream_fec_enable = self.pon.any_upstream_fec_enabled
@property
- def onu_vid(self):
- return self._onu_vid
-
- @property
def uni_ports(self):
return self._uni_ports
@@ -276,52 +210,12 @@
from voltha.protos.device_pb2 import Device
device_id = self.olt.device_id
-
- try:
- if self.olt.xpon_support:
- v_ont_ani = self._vont_ani
- voltha_core = self.olt.adapter_agent.core
- xpon_agent = voltha_core.xpon_agent
- channel_group_id = xpon_agent.get_channel_group_for_vont_ani(v_ont_ani)
- parent_chnl_pair_id = xpon_agent.get_port_num(device_id,
- v_ont_ani.data.preferred_chanpair)
- self._proxy_address = Device.ProxyAddress(
- device_id=device_id,
- channel_group_id=channel_group_id,
- channel_id=parent_chnl_pair_id,
- channel_termination=v_ont_ani.data.preferred_chanpair,
- onu_id=self.onu_id,
- onu_session_id=self.onu_id)
- else:
- self._proxy_address = Device.ProxyAddress(device_id=device_id,
- channel_id=self.pon.port_no,
- onu_id=self.onu_id,
- onu_session_id=self.onu_id)
- except Exception:
- pass
-
+ self._proxy_address = Device.ProxyAddress(device_id=device_id,
+ channel_id=self.pon.port_no,
+ onu_id=self.onu_id,
+ onu_session_id=self.onu_id)
return self._proxy_address
- def _get_v_ont_ani(self, olt):
- onu = None
- try:
- vont_ani = olt.v_ont_anis.get(self.vont_ani)
- ch_pair = olt.channel_pairs.get(vont_ani['preferred-channel-pair'])
- ch_term = next((term for term in olt.channel_terminations.itervalues()
- if term['channel-pair'] == ch_pair['name']), None)
-
- pon = olt.pon(ch_term['xgs-ponid'])
- onu = pon.onu(vont_ani['onu-id'])
-
- except Exception:
- pass
-
- return onu
-
- @property
- def channel_id(self):
- return self._channel_id # SEBA (xPON mode may be only one using this. May also be no one)
-
@property
def serial_number_64(self):
return self._serial_number_base64
@@ -332,6 +226,7 @@
@property
def timestamp(self):
+ # Last time the KPI items were updated
return self._timestamp
@timestamp.setter
@@ -384,8 +279,8 @@
def create(self, tconts, gem_ports, reflow=False):
"""
Create (or reflow) this ONU to hardware
- :param tconts: (TCont) Current TCONT information
- :param gem_ports: (GemPort) Current GEM Port configuration information
+ :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
"""
@@ -399,7 +294,7 @@
name = 'onu-create-{}-{}-{}: {}'.format(self._pon_id, self._onu_id,
self._serial_number_base64, self._enabled)
- first_sync = self._sync_tick if self._created else 3
+ first_sync = self._sync_tick if self._created else 5
if not self._created or reflow:
try:
@@ -421,21 +316,19 @@
except Exception as e:
self.log.warn('onu-exists-check', pon_id=self.pon_id, onu_id=self.onu_id,
serial_number=self.serial_number)
- if self.olt.xpon_support: # xPON mode does rediscovery...
- raise
# Now set up all TConts & GEM-ports
- for _, tcont in tconts.items():
+ for tcont in tconts:
try:
- _results = yield self.add_tcont(tcont, reflow=reflow)
+ _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.items():
+ for gem_port in gem_ports:
try:
- _results = yield self.add_gem_port(gem_port, reflow=reflow)
+ _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)
@@ -457,7 +350,6 @@
self._cancel_deferred()
# Remove from H/W
-
gem_ids = self._gem_ports.keys()
alloc_ids = self._tconts.keys()
@@ -496,7 +388,6 @@
self.log.exception('onu-delete', e=e)
self._olt = None
- self._channel_id = None
returnValue('deleted')
def start(self):
@@ -579,8 +470,8 @@
if alloc_id in matching_alloc_ids}
dl.extend(sync_matching_tconts(matching_hw_tconts))
- except Exception as e:
- self.log.exception('hw-sync-tconts', e=e)
+ except Exception as e2:
+ self.log.exception('hw-sync-tconts', e=e2)
return dl
@@ -625,9 +516,7 @@
my_be.weight != hw_be.weight
if reflow:
- dl.append(my_tcont.add_to_hardware(self.olt.rest_client,
- self._pon_id,
- self._onu_id))
+ dl.append(my_tcont.add_to_hardware(self.olt.rest_client))
return dl
def sync_gem_ports(hw_gem_ports):
@@ -671,8 +560,6 @@
gem_port.encryption != hw_gem_port.encryption or\
gem_port.omci_transport != hw_gem_port.omci_transport:
dl.append(gem_port.add_to_hardware(self.olt.rest_client,
- self.pon.pon_id,
- self.onu_id,
operation='PATCH'))
return dl
@@ -693,7 +580,6 @@
# Speed up sequential resync a limited number of times if out of sync
# With 60 second initial an typical worst case resync of 4 times, this
# should resync an ONU and all it's gem-ports and tconts within <90 seconds
-
if self._expedite_sync and self._enabled:
self._expedite_count += 1
if self._expedite_count < _MAX_EXPEDITE_COUNT:
@@ -707,7 +593,6 @@
# If PON is not enabled, skip hw-sync. If ONU not enabled, do it but less
# frequently
-
if not self.pon.enabled:
return reschedule('not-enabled')
@@ -758,8 +643,7 @@
self._tconts[tcont.alloc_id] = tcont
try:
- results = yield tcont.add_to_hardware(self.olt.rest_client,
- self._pon_id, self._onu_id)
+ results = yield tcont.add_to_hardware(self.olt.rest_client)
except Exception as e:
self.log.exception('tcont', tcont=tcont, reflow=reflow, e=e)
@@ -777,9 +661,7 @@
tcont.traffic_descriptor = new_td
try:
- results = yield tcont.add_to_hardware(self.olt.rest_client,
- self._pon_id,
- self._onu_id)
+ 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
@@ -796,9 +678,7 @@
del self._tconts[alloc_id]
try:
- results = yield tcont.remove_from_hardware(self.olt.rest_client,
- self._pon_id,
- self._onu_id)
+ results = yield tcont.remove_from_hardware(self.olt.rest_client)
except RestInvalidResponseCode as e:
results = None
if e.code != 404:
@@ -813,15 +693,10 @@
def gem_port(self, gem_id):
return self._gem_ports.get(gem_id)
- def gem_ids(self, untagged_gem):
+ def gem_ids(self):
"""Get all GEM Port IDs used by this ONU"""
- if untagged_gem:
- gem_ids = sorted([gem_id for gem_id, gem in self._gem_ports.items()
- if gem.untagged and not gem.multicast])
- return gem_ids
- else:
- return sorted([gem_id for gem_id, gem in self._gem_ports.items()
- if not gem.multicast and not gem.untagged])
+ return sorted([gem_id for gem_id, gem in self._gem_ports.items()
+ if not gem.multicast])
@inlineCallbacks
def add_gem_port(self, gem_port, reflow=False):
@@ -842,13 +717,13 @@
gem_port.onu_id = self.onu_id if self.onu_id is not None else -1
gem_port.intf_id = self.intf_id
+ # TODO: Currently only support a single UNI. Need to support multiple and track their GEM Ports
+ # Probably best done by having a UNI-Port class (keep it simple)
self.log.info('add', gem_port=gem_port, reflow=reflow)
self._gem_ports[gem_port.gem_id] = gem_port
try:
- results = yield gem_port.add_to_hardware(self.olt.rest_client,
- self._pon_id,
- self.onu_id)
+ 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
@@ -886,9 +761,8 @@
for evc_map in evc_maps:
evc_map.remove_gem_port(gem_port)
- yield gem_port.remove_from_hardware(self.olt.rest_client,
- self._pon_id,
- self.onu_id)
+ yield gem_port.remove_from_hardware(self.olt.rest_client)
+
except RestInvalidResponseCode as e:
if e.code != 404:
self.log.exception('onu-delete', e=e)
diff --git a/voltha/adapters/adtran_olt/pon_port.py b/voltha/adapters/adtran_olt/pon_port.py
index dd1b7e3..0b18dc2 100644
--- a/voltha/adapters/adtran_olt/pon_port.py
+++ b/voltha/adapters/adtran_olt/pon_port.py
@@ -20,7 +20,6 @@
from port import AdtnPort
from twisted.internet import reactor, defer
from twisted.internet.defer import inlineCallbacks, returnValue
-from common.utils.indexpool import IndexPool
from adtran_olt_handler import AdtranOltHandler
from net.adtran_rest import RestInvalidResponseCode
from codec.olt_config import OltConfig
@@ -28,6 +27,12 @@
from voltha.extensions.alarms.onu.onu_los_alarm import OnuLosAlarm
from voltha.protos.common_pb2 import AdminState
from voltha.protos.device_pb2 import Port
+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
+from xpon.olt_traffic_descriptor import OltTrafficDescriptor
+import resources.adtranolt_platform as platform
+
try:
from voltha.extensions.alarms.onu.onu_discovery_alarm import OnuDiscoveryAlarm
@@ -39,8 +44,7 @@
"""
GPON Port
"""
- MAX_ONUS_SUPPORTED = 256
- DEFAULT_ENABLED = False
+ MAX_ONUS_SUPPORTED = 128
MAX_DEPLOYMENT_RANGE = 25000 # Meters (OLT-PB maximum)
_MCAST_ONU_ID = 253
@@ -52,7 +56,6 @@
def __init__(self, parent, **kwargs):
super(PonPort, self).__init__(parent, **kwargs)
-
assert 'pon-id' in kwargs, 'PON ID not found'
self._parent = parent
@@ -69,36 +72,34 @@
self._discovery_tick = 20.0
self._no_onu_discover_tick = self._discovery_tick / 2
self._discovered_onus = [] # List of serial numbers
+ self._discovery_deferred = None # Specifically for ONU discovery
- self._onus = {} # serial_number-base64 -> ONU (allowed list)
- self._onu_by_id = {} # onu-id -> ONU
- self._next_onu_id = Onu.MIN_ONU_ID
- self._mcast_gem_ports = {} # VLAN -> GemPort
- self._onu_id_pool = IndexPool(Onu.MAX_ONU_ID - Onu.MIN_ONU_ID + 1, Onu.MIN_ONU_ID)
+ self._onus = {} # serial_number-base64 -> ONU (allowed list)
+ self._onu_by_id = {} # onu-id -> ONU
+ self._mcast_gem_ports = {} # VLAN -> GemPort
- self._discovery_deferred = None # Specifically for ONU discovery
- self._active_los_alarms = set() # ONU-ID
+ self._active_los_alarms = set() # ONU-ID
- # xPON configuration # SEBA
- self._activation_method = 'autodiscovery' if parent.xpon_support else 'autoactivate'
+ # xPON configuration
+ self._activation_method = 'autoactivate'
- if self.olt.xpon_support:
- self._xpon_name = None
- self._downstream_fec_enable = False
- self._upstream_fec_enable = False
- else:
- self._xpon_name = 'channel-termination {}'.format(self._pon_id)
- self._downstream_fec_enable = True
- self._upstream_fec_enable = True
-
+ self._downstream_fec_enable = True
+ self._upstream_fec_enable = True
self._deployment_range = 25000
self._authentication_method = 'serial-number'
self._mcast_aes = False
- self._line_rate = 'down_10_up_10'
# 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._traffic_descriptors = {} # Name -> dict
+ self._gem_ports = {} # Name -> dict
+
def __str__(self):
return "PonPort-{}: Admin: {}, Oper: {}, OLT: {}".format(self._label,
self._admin_state,
@@ -120,15 +121,6 @@
return self._port
@property
- def xpon_name(self):
- return self._xpon_name
-
- @xpon_name.setter
- def xpon_name(self, value):
- assert '/' not in value, "xPON names cannot have embedded forward slashes '/'"
- self._xpon_name = value
-
- @property
def pon_id(self):
return self._pon_id
@@ -211,19 +203,6 @@
pass # TODO
@property
- def line_rate(self):
- return self._line_rate
-
- @line_rate.setter
- def line_rate(self, value):
- assert isinstance(value, (str, unicode)), 'Line Rate is a string'
- # TODO cast to enum
- if self._line_rate != value:
- self._line_rate = value
- if self.state == AdtnPort.State.RUNNING:
- pass # TODO
-
- @property
def deployment_range(self):
"""Maximum deployment range (in meters)"""
return self._deployment_range
@@ -273,9 +252,6 @@
if value not in PonPort._SUPPORTED_ACTIVATION_METHODS:
raise ValueError('Invalid ONU activation method')
- if not self._parent.xpon_support and value != 'autoactivate':
- raise ValueError('Only autoactivate is supported in non-xPON mode')
-
self._activation_method = value
@property
@@ -434,8 +410,7 @@
Set the PON Port to a known good state on initial port startup. Actual
PON 'Start' is done elsewhere
"""
- initial_port_state = AdminState.DISABLED if self._parent.xpon_support \
- else AdminState.ENABLED
+ initial_port_state = AdminState.ENABLED
self.log.info('reset', initial_state=initial_port_state)
try:
@@ -481,14 +456,14 @@
returnValue('Reset complete')
- def gem_ids(self, logical_port, untagged_gem, multicast_gems=False):
+ def gem_ids(self, logical_port, flow_vlan, multicast_gems=False):
"""
Get all GEM Port IDs used on a given PON
- :param logical_port: (int) Logical port umber of ONU. None if for all ONUs
+ :param logical_port: (int) Logical port number of ONU. None if for all ONUs
on PON, if Multicast, VID for Multicast, or None for all
Multicast GEMPorts
- :param untagged_gem: (boolean) Select from special purpose untagged GEM Port
+ :param flow_vlan: (int) If not None, this is the ingress tag (c-tag)
:param multicast_gems: (boolean) Select from available Multicast GEM Ports
:return: (dict) data_gem -> key -> onu-id, value -> tuple(sorted list of GEM Port IDs, onu_vid)
mcast_gem-> key -> mcast-vid, value -> GEM Port IDs
@@ -497,16 +472,14 @@
if multicast_gems:
# Multicast GEMs belong to the PON, but we may need to register them on
- # all ONUs. Rework when BBF MCAST Gems are supported
- for vlan, gem_port in self._mcast_gem_ports.iteritems(): # TODO: redo logic
+ # all ONUs. TODO: Rework when BBF MCAST is addressed in VOLTHA v2.O+
+ for vlan, gem_port in self._mcast_gem_ports.iteritems():
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(untagged_gem),
- onu.onu_vid if not untagged_gem
- else self.olt.untagged_vlan)
+ gem_ids[onu_id] = (onu.gem_ids(), flow_vlan)
return gem_ids
def _get_pon_config(self):
@@ -663,6 +636,7 @@
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)
@@ -677,44 +651,6 @@
# Process GEM Port information
self._update_gem_status(status.gems, timestamp)
- def _handle_discovered_onu(self, child_device, ind_info):
- pon_id = ind_info['_pon_id']
- olt_id = ind_info['_olt_id']
-
- if ind_info['_sub_group_type'] == 'onu_discovery':
- self.log.info('Activation-is-in-progress', olt_id=olt_id,
- pon_ni=pon_id, onu_data=ind_info,
- onu_id=child_device.proxy_address.onu_id)
-
- elif ind_info['_sub_group_type'] == 'sub_term_indication':
- self.log.info('ONU-activation-is-completed', olt_id=olt_id,
- pon_ni=pon_id, onu_data=ind_info)
-
- msg = {'proxy_address': child_device.proxy_address,
- 'event': 'activation-completed', 'event_data': ind_info}
-
- # Send the event message to the ONU adapter
- self.adapter_agent.publish_inter_adapter_message(child_device.id,
- msg)
- if ind_info['activation_successful'] is True:
- for key, v_ont_ani in dict(): # self.v_ont_anis.items():
- if v_ont_ani.v_ont_ani.data.onu_id == \
- child_device.proxy_address.onu_id:
- for tcont_key, tcont in v_ont_ani.tconts.items():
- owner_info = dict()
- # To-Do: Right Now use alloc_id as schduler ID. Need to
- # find way to generate uninqe number.
- id = tcont.alloc_id
- owner_info['type'] = 'agg_port'
- owner_info['intf_id'] = \
- child_device.proxy_address.channel_id
- owner_info['onu_id'] = \
- child_device.proxy_address.onu_id
- owner_info['alloc_id'] = tcont.alloc_id
- else:
- self.log.info('Invalid-ONU-event', olt_id=olt_id,
- pon_ni=ind_info['_pon_id'], onu_data=ind_info)
-
def _process_statistics(self, status, timestamp):
self.timestamp = timestamp
self.rx_packets = status.rx_packets
@@ -797,69 +733,26 @@
:param serial_number: (string) Decoded (not base64) serial number string
:return: (dict) onu config data or None on lookup failure
"""
- activate_onu = False
try:
if self.activation_method == "autodiscovery":
- if self.authentication_method == 'serial-number':
- gpon_info = self.olt.get_xpon_info(self.pon_id)
- try:
- # TODO: Change iteration to itervalues below
- vont_info = next(info for _, info in gpon_info['vont-anis'].items()
- if info.get('expected-serial-number') == serial_number)
+ # if self.authentication_method == 'serial-number':
+ raise NotImplemented('TODO: Not supported at this time')
- # ont_info = next(info for _, info in gpon_info['ont-anis'].items()
- # if info.get('name') == vont_info['name'])
-
- vont_ani = vont_info['data']
- onu_id = vont_info['onu-id']
- enabled = vont_info['enabled']
- channel_speed = vont_info['upstream-channel-speed']
- xpon_name = vont_info['name']
- upstream_fec_enabled = True # TODO: ont_info.get('upstream-fec', False)
-
- tconts = {key: val for key, val in gpon_info['tconts'].iteritems()
- if val.vont_ani == vont_info['name']}
-
- gem_ports = {key: val for key, val in gpon_info['gem-ports'].iteritems()
- if val.tcont_ref in tconts.keys()}
-
- venet = next((val for val in gpon_info['v-enets'].itervalues()
- if val['vont-ani'] == vont_info['name']), None)
- # TODO: need to handle case where ont_ani, gems, venets, tconts are assigned
- # after activation is started. only vont-ani needs to be set to get here
-
- except StopIteration:
- # Can happen if vont-ani or ont-ani has not yet been configured
- self.log.debug('no-vont-or-ont')
- return None, False
-
- except Exception as e:
- self.log.exception('autodiscovery', e=e)
- raise
- else:
- self.log.debug('not-serial-number-authentication')
- return None, False
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
- gpon_info, activate_onu = self.olt.get_device_profile_info(self,
- serial_number)
+ activate_onu, tconts, gem_ports = self.get_device_profile_info(serial_number)
+
try:
# TODO: (SEBA) All of this can be greatly simplified once we fully
# deprecate xPON support
- vont_ani = next(info for _, info in gpon_info['vont-anis'].items()
+ vont_ani = next(info for _, info in self._v_ont_anis.items()
if info.get('expected-serial-number') == serial_number)
- # ont_ani = gpon_info['ont-anis'][vont_ani['name']]
onu_id = vont_ani['onu-id']
enabled = vont_ani['enabled']
channel_speed = vont_ani['upstream-channel-speed']
- xpon_name = vont_ani['name']
- upstream_fec_enabled = gpon_info['ont-anis'][vont_ani['name']]['upstream-fec']
-
- tconts = gpon_info['tconts']
- gem_ports = gpon_info['gem-ports']
- venet = None
+ 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
@@ -876,7 +769,6 @@
onu_info = {
'device-id': self.olt.device_id,
'serial-number': serial_number,
- 'xpon-name': xpon_name,
'pon': self,
'onu-id': onu_id,
'enabled': enabled,
@@ -885,17 +777,11 @@
'password': Onu.DEFAULT_PASSWORD,
't-conts': tconts,
'gem-ports': gem_ports,
- 'channel-id': self.olt.get_channel_id(self._pon_id, onu_id), # TODO: Is this used anywhere?
- 'vont-ani': vont_ani,
- 'venet': venet
}
- if self.olt.xpon_support:
- onu_info['onu-vid'] = self.olt.get_onu_vid(onu_id)
- else:
- import adtranolt_platform as platform
- intf_id = platform.intf_id_to_port_no(self._pon_id, Port.PON_OLT)
- # 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)]
+ intf_id = platform.intf_id_to_port_no(self._pon_id, Port.PON_OLT)
+
+ # 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)
@@ -918,27 +804,16 @@
self.log.info('add-onu', serial_number=serial_number,
serial_number_64=serial_number_64, status=status)
- # For non-XPON mode, it takes a little while for a new ONU to be removed from
- # the discovery list. Return early here so extra ONU IDs are not allocated
- if not self.olt.xpon_support and serial_number_64 in self._onus:
+ # It takes a little while for a new ONU to be removed from the discovery
+ # list. Return early here so extra ONU IDs are not allocated
+ if serial_number_64 in self._onus:
returnValue('wait-for-fpga')
onu_info, activate_onu = self._get_onu_info(serial_number)
if activate_onu:
- # SEBA - This is the new no-xPON way
alarm = OnuDiscoveryAlarm(self.olt.alarms, self.pon_id, serial_number)
reactor.callLater(0, alarm.raise_alarm)
- # Fall through. We do not want to return and wait for the next discovery poll
- # as that will consume an additional ONU ID (currently allocated during
- # add_onu_device).
-
- elif onu_info is None:
- # SEBA - This is the OLD xPON way
- self.log.info('onu-lookup-failure', serial_number=serial_number,
- serial_number_64=serial_number_64)
- OnuDiscoveryAlarm(self.olt.alarms, self.pon_id, serial_number).raise_alarm()
- returnValue('new-onu')
if serial_number_64 not in status.onus or onu_info['onu-id'] in self._active_los_alarms:
onu = None
@@ -952,17 +827,14 @@
(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.
- if self.olt.xpon_support: # xPON will reassign the same ONU ID
- # Drop it and add back on next discovery cycle
- self.delete_onu(onu_id)
- else:
- # 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: 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
elif len(self._onus) >= self.MAX_ONUS_SUPPORTED:
self.log.warning('max-onus-provisioned', count=len(self._onus))
@@ -975,30 +847,31 @@
self._onu_by_id[onu.onu_id] = onu
if onu is not None:
- tconts = onu_info['t-conts']
- gem_ports = onu_info['gem-ports']
+ tconts = onu_info.pop('t-conts')
+ gem_ports = onu_info.pop('gem-ports')
if activate_onu:
- # SEBA - This is the new no-xPON way to start an ONU device handler
- _onu_device = self._parent.add_onu_device(self._port_no, # PON ID
- onu_info['onu-id'], # ONU ID
+ _onu_device = self._parent.add_onu_device(self._port_no, # PON ID
+ onu.onu_id, # ONU ID
serial_number,
- tconts, gem_ports)
+ tconts,
+ gem_ports)
try:
- # Add Multicast to PON on a per-ONU basis until xPON multicast support is ready
+ # 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
- 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)
+ # 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)
@@ -1009,12 +882,12 @@
@property
def get_next_onu_id(self):
- assert not self.olt.xpon_support, 'Only non-XPON mode allocates ONU IDs. xPON assigns tem'
- return self._onu_id_pool.get_next()
+ return self._parent.resource_mgr.get_onu_id(self._pon_id)
+ # return self._onu_id_pool.get_next()
def release_onu_id(self, onu_id):
- if not self.olt.xpon_support:
- self._onu_id_pool.release(onu_id)
+ self._parent.resource_mgr.free_onu_id(self._pon_id, onu_id)
+ # self._onu_id_pool.release(onu_id)
@inlineCallbacks
def _remove_from_hardware(self, onu_id):
@@ -1071,3 +944,124 @@
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 traffic_descriptors(self):
+ return self._traffic_descriptors
+
+ @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, 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.gemport_id = platform.mk_gemport_id(self.pon_id, onu_id, idx=index)
+ gem_port.name = 'gem-{}-{}-{}'.format(self.pon_id, onu_id, gem_port.gemport_id)
+
+ 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/port.py b/voltha/adapters/adtran_olt/port.py
index 1eb0a13..9509556 100644
--- a/voltha/adapters/adtran_olt/port.py
+++ b/voltha/adapters/adtran_olt/port.py
@@ -56,12 +56,8 @@
# TODO: Deprecate 'enabled' and use admin_state instead may want initial to always be
# disabled and then in derived classes, set it in the 'reset' method called on startup.
- if parent.xpon_support:
- self._enabled = not parent.xpon_support
- self._admin_state = AdminState.DISABLED
- else:
- self._enabled = not parent.xpon_support
- self._admin_state = AdminState.ENABLED
+ self._enabled = True
+ self._admin_state = AdminState.ENABLED
self._oper_status = OperStatus.DISCOVERED
self._state = AdtnPort.State.INITIAL
@@ -73,7 +69,7 @@
self.rx_bytes = 0
self.tx_packets = 0
self.tx_bytes = 0
- self.timestamp = 0
+ self.timestamp = 0 # UTC when KPI items last updated
def __del__(self):
self.stop()
diff --git a/voltha/adapters/adtran_olt/resources/__init__.py b/voltha/adapters/adtran_olt/resources/__init__.py
new file mode 100644
index 0000000..9c454e3
--- /dev/null
+++ b/voltha/adapters/adtran_olt/resources/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2018-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.
+#
diff --git a/voltha/adapters/adtran_olt/resources/adtran_olt_resource_manager.py b/voltha/adapters/adtran_olt/resources/adtran_olt_resource_manager.py
new file mode 100644
index 0000000..97ec852
--- /dev/null
+++ b/voltha/adapters/adtran_olt/resources/adtran_olt_resource_manager.py
@@ -0,0 +1,211 @@
+#
+# 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.pon_resource_manager.resource_manager import PONResourceManager
+from voltha.registry import registry
+from voltha.core.config.config_backend import ConsulStore
+from voltha.core.config.config_backend import EtcdStore
+from adtran_resource_manager import AdtranPONResourceManager
+
+
+class AdtranOltResourceMgr(object):
+
+ GEMPORT_IDS = "gemport_ids"
+ ALLOC_IDS = "alloc_ids"
+ BASE_PATH_KV_STORE = "adtran_olt/{}" # adtran_olt/<device_id>
+
+ def __init__(self, device_id, host_and_port, extra_args, device_info):
+ self.log = structlog.get_logger(id=device_id,
+ ip=host_and_port)
+ self.device_id = device_id
+ self.host_and_port = host_and_port
+ self.extra_args = extra_args
+ self.device_info = device_info
+ self.args = registry('main').get_args()
+
+ # KV store's IP Address and PORT
+ # host, port = '127.0.0.1', 8500
+ if self.args.backend == 'etcd':
+ host, port = self.args.etcd.split(':', 1)
+ self.kv_store = EtcdStore(host, port,
+ AdtranOltResourceMgr.BASE_PATH_KV_STORE.format(device_id))
+ elif self.args.backend == 'consul':
+ host, port = self.args.consul.split(':', 1)
+ self.kv_store = ConsulStore(host, port,
+ AdtranOltResourceMgr.BASE_PATH_KV_STORE.format(device_id))
+ else:
+ self.log.error('Invalid-backend')
+ raise Exception("Invalid-backend-for-kv-store")
+
+ self.resource_mgr = AdtranPONResourceManager(
+ self.device_info.technology,
+ self.extra_args,
+ self.device_id, self.args.backend,
+ host, port
+ )
+ # Flag to indicate whether information fetched from device should
+ # be used to initialize PON Resource Ranges
+ self.use_device_info = False
+
+ self.initialize_device_resource_range_and_pool()
+
+ def __del__(self):
+ self.log.info("clearing-device-resource-pool")
+ self.resource_mgr.clear_device_resource_pool()
+
+ def get_onu_id(self, pon_intf_id):
+ onu_id = self.resource_mgr.get_resource_id(pon_intf_id,
+ PONResourceManager.ONU_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)
+
+ return onu_id
+
+ def free_onu_id(self, pon_intf_id, onu_id):
+ self.resource_mgr.free_resource_id(pon_intf_id,
+ PONResourceManager.ONU_ID,
+ onu_id)
+ pon_intf_onu_id = (pon_intf_id, onu_id)
+ self.resource_mgr.remove_resource_map(pon_intf_onu_id)
+
+ def get_alloc_id(self, pon_intf_onu_id):
+ # Derive the pon_intf from the pon_intf_onu_id tuple
+ pon_intf = pon_intf_onu_id[0]
+ onu_id = pon_intf_onu_id[1]
+ alloc_id_list = self.resource_mgr.get_current_alloc_ids_for_onu(pon_intf_onu_id)
+
+ if alloc_id_list and len(alloc_id_list) > 0:
+ # Since we support only one alloc_id for the ONU at the moment,
+ # return the first alloc_id in the list, if available, for that
+ # 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
+ )
+ if alloc_id_list and len(alloc_id_list) == 0:
+ self.log.error("no-alloc-id-available")
+ return None
+
+ # update the resource map on KV store with the list of alloc_id
+ # allocated for the pon_intf_onu_id tuple
+ self.resource_mgr.update_alloc_ids_for_onu(pon_intf_onu_id,
+ alloc_id_list)
+
+ # Since we request only one alloc id, we refer the 0th
+ # index
+ alloc_id = alloc_id_list[0]
+
+ return alloc_id
+
+ def get_gemport_id(self, pon_intf_onu_id):
+ # 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]
+
+ gemport_id_list = self.resource_mgr.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]
+
+ gemport_id_list = self.resource_mgr.get_resource_id(
+ pon_intf_id=pon_intf,
+ resource_type=PONResourceManager.GEMPORT_ID,
+ num_of_id=1
+ )
+ 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]
+
+ 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
+
+ def free_pon_resources_for_onu(self, pon_intf_id_onu_id):
+
+ alloc_ids = \
+ self.resource_mgr.get_current_alloc_ids_for_onu(pon_intf_id_onu_id)
+ pon_intf_id = pon_intf_id_onu_id[0]
+ onu_id = pon_intf_id_onu_id[1]
+ self.resource_mgr.free_resource_id(pon_intf_id,
+ PONResourceManager.ALLOC_ID,
+ alloc_ids)
+
+ gemport_ids = \
+ self.resource_mgr.get_current_gemport_ids_for_onu(pon_intf_id_onu_id)
+ self.resource_mgr.free_resource_id(pon_intf_id,
+ PONResourceManager.GEMPORT_ID,
+ gemport_ids)
+
+ self.resource_mgr.free_resource_id(pon_intf_id,
+ PONResourceManager.ONU_ID,
+ onu_id)
+
+ # Clear resource map associated with (pon_intf_id, gemport_id) tuple.
+ self.resource_mgr.remove_resource_map(pon_intf_id_onu_id)
+
+ # Clear the ONU Id associated with the (pon_intf_id, gemport_id) tuple.
+ for gemport_id in gemport_ids:
+ del self.kv_store[str((pon_intf_id, gemport_id))]
+
+ def initialize_device_resource_range_and_pool(self):
+ if not self.use_device_info:
+ status = self.resource_mgr.init_resource_ranges_from_kv_store()
+ if not status:
+ self.log.error("failed-to-load-resource-range-from-kv-store")
+ # When we have failed to read the PON Resource ranges from KV
+ # store, use the information selected as the default.
+ self.use_device_info = True
+
+ if self.use_device_info:
+ self.log.info("using-device-info-to-init-pon-resource-ranges")
+ self.resource_mgr.init_default_pon_resource_ranges(
+ onu_id_start_idx=self.device_info.onu_id_start,
+ onu_id_end_idx=self.device_info.onu_id_end,
+ alloc_id_start_idx=self.device_info.alloc_id_start,
+ alloc_id_end_idx=self.device_info.alloc_id_end,
+ gemport_id_start_idx=self.device_info.gemport_id_start,
+ gemport_id_end_idx=self.device_info.gemport_id_end,
+ num_of_pon_ports=self.device_info.pon_ports,
+ intf_ids=self.device_info.intf_ids
+ )
+
+ # After we have initialized resource ranges, initialize the
+ # resource pools accordingly.
+ self.resource_mgr.init_device_resource_pool()
diff --git a/voltha/adapters/adtran_olt/resources/adtran_resource_manager.py b/voltha/adapters/adtran_olt/resources/adtran_resource_manager.py
new file mode 100644
index 0000000..83f8e33
--- /dev/null
+++ b/voltha/adapters/adtran_olt/resources/adtran_resource_manager.py
@@ -0,0 +1,351 @@
+#
+# 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.
+#
+
+"""
+Resource Manager will be unique for each OLT device.
+
+It exposes APIs to create/free alloc_ids/onu_ids/gemport_ids. Resource Manager
+uses a KV store in backend to ensure resiliency of the data.
+"""
+from bitstring import BitArray
+import json
+from common.pon_resource_manager.resource_manager import PONResourceManager
+import adtranolt_platform as platform
+
+
+class AdtranPONResourceManager(PONResourceManager):
+ """Implements APIs to initialize/allocate/release alloc/gemport/onu IDs."""
+
+ # Constants for internal usage.
+ ONU_MAP = 'onu_map'
+
+ def init_device_resource_pool(self):
+ """
+ Initialize resource pool for all PON ports.
+ """
+ for pon_id in self.intf_ids:
+ self.init_resource_id_pool(
+ pon_intf_id=pon_id,
+ resource_type=PONResourceManager.ONU_ID,
+ start_idx=self.pon_resource_ranges[PONResourceManager.ONU_ID_START_IDX],
+ end_idx=self.pon_resource_ranges[PONResourceManager.ONU_ID_END_IDX])
+
+ alloc_id_map = dict()
+ for onu_id in range(platform.MAX_ONUS_PER_PON):
+ alloc_id_map[onu_id] = [platform.mk_alloc_id(pon_id, onu_id, idx)
+ for idx in xrange(platform.MAX_TCONTS_PER_ONU)]
+
+ self.init_resource_id_pool(pon_intf_id=pon_id,
+ resource_type=PONResourceManager.ALLOC_ID,
+ resource_map=alloc_id_map)
+
+ # TODO: I believe that only alloc_id's are constrained to ONU ID. If so, use
+ # the generic pool approach
+ # gemport_id_map = dict()
+ # for onu_id in range(platform.MAX_GEM_PORTS_PER_ONU):
+ # gemport_id_map[onu_id] = [platform.mk_gemport_id(pon_id, onu_id, idx)
+ # for idx in xrange(platform.MAX_TCONTS_PER_ONU)]
+ #
+ # self.init_resource_id_pool(pon_intf_id=pon_id,
+ # resource_type=PONResourceManager.GEMPORT_ID,
+ # resource_map=gemport_id_map)
+ self.init_resource_id_pool(
+ pon_intf_id=pon_id,
+ resource_type=PONResourceManager.GEMPORT_ID,
+ start_idx=self.pon_resource_ranges[PONResourceManager.GEMPORT_ID_START_IDX],
+ end_idx=self.pon_resource_ranges[PONResourceManager.GEMPORT_ID_START_IDX])
+
+ def clear_device_resource_pool(self):
+ """
+ Clear resource pool of all PON ports.
+ """
+ for pon_id in self.intf_ids:
+ self.clear_resource_id_pool(pon_intf_id=pon_id,
+ resource_type=PONResourceManager.ONU_ID)
+
+ self.clear_resource_id_pool(
+ pon_intf_id=pon_id,
+ resource_type=PONResourceManager.ALLOC_ID,
+ )
+
+ self.clear_resource_id_pool(
+ pon_intf_id=pon_id,
+ resource_type=PONResourceManager.GEMPORT_ID,
+ )
+
+ def init_resource_id_pool(self, pon_intf_id, resource_type, start_idx=None,
+ end_idx=None, resource_map=None):
+ """
+ Initialize Resource ID pool for a given Resource Type on a given PON Port
+
+ :param pon_intf_id: OLT PON interface id
+ :param resource_type: String to identify type of resource
+ :param start_idx: start index for onu id pool
+ :param end_idx: end index for onu id pool
+ :param resource_map: (dict) Resource map if per-ONU specific
+ :return boolean: True if resource id pool initialized else false
+ """
+ status = False
+ path = self._get_path(pon_intf_id, resource_type)
+ if path is None:
+ return status
+
+ try:
+ # In case of adapter reboot and reconciliation resource in kv store
+ # checked for its presence if not kv store update happens
+ resource = self._get_resource(path)
+
+ if resource is not None:
+ self._log.info("Resource-already-present-in-store", path=path)
+ status = True
+
+ else:
+ if resource_map is None:
+ resource = self._format_resource(pon_intf_id, start_idx, end_idx)
+ self._log.info("Resource-initialized", path=path)
+
+ else:
+ resource = self._format_map_resource(pon_intf_id, resource_map)
+
+ # Add resource as json in kv store.
+ status = self._kv_store.update_to_kv_store(path, resource)
+
+ except Exception as e:
+ self._log.exception("error-initializing-resource-pool", e=e)
+
+ return status
+
+ def _generate_next_id(self, resource, onu_id=None):
+ """
+ Generate unique id having OFFSET as start index.
+
+ :param resource: resource used to generate ID
+ :return int: generated id
+ """
+ if onu_id is not None:
+ resource = resource[AdtranPONResourceManager.ONU_MAP][str(onu_id)]
+
+ pos = resource[PONResourceManager.POOL].find('0b0')
+ resource[PONResourceManager.POOL].set(1, pos)
+ return pos[0] + resource[PONResourceManager.START_IDX]
+
+ def _release_id(self, resource, unique_id, onu_id=None):
+ """
+ Release unique id having OFFSET as start index.
+
+ :param resource: resource used to release ID
+ :param unique_id: id need to be released
+ :param onu_id: ONU ID if unique per ONU
+ """
+ if onu_id is not None:
+ resource = resource[AdtranPONResourceManager.ONU_MAP][str(onu_id)]
+
+ pos = ((int(unique_id)) - resource[PONResourceManager.START_IDX])
+ resource[PONResourceManager.POOL].set(0, pos)
+
+ def get_resource_id(self, pon_intf_id, resource_type, onu_id=None, num_of_id=1):
+ """
+ Create alloc/gemport/onu id for given OLT PON interface.
+
+ :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
+ :return list/int/None: list, int or None if resource type is
+ alloc_id/gemport_id, onu_id or invalid type
+ respectively
+ """
+ result = None
+
+ 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:
+ 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_type == PONResourceManager.ALLOC_ID:
+ result = [self._generate_next_id(resource, onu_id) for _ in range(num_of_id)]
+
+ self._log.debug("Get-" + resource_type + "-success", result=result,
+ path=path)
+ # Update resource in kv store
+ self._update_resource(path, resource, onu_id=onu_id)
+
+ except Exception as e:
+ self._log.exception("Get-" + resource_type + "-id-failed",
+ path=path, e=e)
+ return result
+
+ def free_resource_id(self, pon_intf_id, resource_type, release_content, onu_id=None):
+ """
+ Release alloc/gemport/onu id for given OLT PON interface.
+
+ :param pon_intf_id: OLT PON interface id
+ :param resource_type: String to identify type of resource
+ :param release_content: required number of ids
+ :param onu_id: ONU ID if unique per ONU
+ :return boolean: True if all IDs in given release_content released
+ else False
+ """
+ status = False
+
+ path = self._get_path(pon_intf_id, resource_type)
+ if path is None:
+ return status
+
+ try:
+ resource = self._get_resource(path, onu_id=onu_id)
+ if resource is None:
+ raise Exception("get-resource-failed")
+
+ if resource_type == PONResourceManager.ONU_ID:
+ self._release_id(resource, release_content)
+
+ elif resource_type == PONResourceManager.ALLOC_ID:
+ for content in release_content:
+ self._release_id(resource, content)
+
+ elif resource_type == PONResourceManager.GEMPORT_ID:
+ for content in release_content:
+ self._release_id(resource, content, onu_id)
+ else:
+ raise Exception("get-resource-failed")
+
+ self._log.debug("Free-" + resource_type + "-success", path=path)
+
+ # Update resource in kv store
+ status = self._update_resource(path, resource, onu_id=onu_id)
+
+ except Exception as e:
+ self._log.exception("Free-" + resource_type + "-failed",
+ path=path, e=e)
+ return status
+
+ def _update_resource(self, path, resource, onu_id=None):
+ """
+ Update resource in resource kv store.
+
+ :param path: path to update resource
+ :param resource: resource need to be updated
+ :return boolean: True if resource updated in kv store else False
+ """
+ if 'alloc_id' in path.lower():
+ assert onu_id is not None
+ poolResource = resource[AdtranPONResourceManager.ONU_MAP][str(onu_id)]
+ poolResource[PONResourceManager.POOL] = \
+ poolResource[PONResourceManager.POOL].bin
+ else:
+ resource[PONResourceManager.POOL] = \
+ resource[PONResourceManager.POOL].bin
+
+ return self._kv_store.update_to_kv_store(path, json.dumps(resource))
+
+ def _get_resource(self, path, onu_id=None):
+ """
+ Get resource from kv store.
+
+ :param path: path to get resource
+ :return: resource if resource present in kv store else None
+ """
+ # get resource from kv store
+ result = self._kv_store.get_from_kv_store(path)
+ if result is None:
+ return result
+
+ self._log.info("dumping-resource", result=result)
+ resource = result
+
+ if resource is not None:
+ # decode resource fetched from backend store to dictionary
+ resource = json.loads(resource)
+
+ if 'alloc_id' in path.lower():
+ assert onu_id is not None
+ poolResource = resource[AdtranPONResourceManager.ONU_MAP][str(onu_id)]
+ poolResource[PONResourceManager.POOL] = \
+ BitArray('0b' + poolResource[PONResourceManager.POOL])
+ else:
+ # resource pool in backend store stored as binary string whereas to
+ # access the pool to generate/release IDs it need to be converted
+ # as BitArray
+ resource[PONResourceManager.POOL] = \
+ BitArray('0b' + resource[PONResourceManager.POOL])
+
+ return resource
+
+ def _format_resource(self, pon_intf_id, start_idx, end_idx):
+ """
+ Format resource as json.
+
+ :param pon_intf_id: OLT PON interface id
+ :param start_idx: start index for id pool
+ :param end_idx: end index for id pool
+ :return dictionary: resource formatted as dictionary
+ """
+ # Format resource as json to be stored in backend store
+ resource = dict()
+ resource[PONResourceManager.PON_INTF_ID] = pon_intf_id
+ resource[PONResourceManager.START_IDX] = start_idx
+ resource[PONResourceManager.END_IDX] = end_idx
+
+ # resource pool stored in backend store as binary string
+ resource[PONResourceManager.POOL] = BitArray(end_idx-start_idx).bin
+
+ return json.dumps(resource)
+
+ def _format_map_resource(self, pon_intf_id, resource_map):
+ """
+ Format resource as json.
+ # TODO: Refactor the resource BitArray to be just a list of the resources.
+ # This is used to store available alloc-id's on a per-onu/pon basis
+ # which in BitArray string form, is a 768 byte string for just 4 possible
+ # alloc-IDs. This equates to 1.57 MB of storage when you take into
+ # account 128 ONUs and 16 PONs pre-provisioneed
+ :param pon_intf_id: OLT PON interface id
+ :param resource_map: (dict) ONU ID -> Scattered list of IDs
+ :return dictionary: resource formatted as dictionary
+ """
+ # Format resource as json to be stored in backend store
+ resource = dict()
+ resource[PONResourceManager.PON_INTF_ID] = pon_intf_id
+
+ onu_dict = dict()
+ for onu_id, resources in resource_map.items():
+ start_idx = min(resources)
+ end_idx = max(resources) + 1
+
+ onu_dict[onu_id] = {
+ PONResourceManager.START_IDX: start_idx,
+ PONResourceManager.END_IDX: end_idx,
+ }
+ # Set non-allowed values as taken
+ resource_map = BitArray(end_idx - start_idx)
+ not_available = {pos for pos in xrange(end_idx-start_idx)
+ if pos + start_idx not in resources}
+ resource_map.set(True, not_available)
+ onu_dict[onu_id][PONResourceManager.POOL] = resource_map.bin
+
+ resource[AdtranPONResourceManager.ONU_MAP] = onu_dict
+ return json.dumps(resource)
diff --git a/voltha/adapters/adtran_olt/adtranolt_platform.py b/voltha/adapters/adtran_olt/resources/adtranolt_platform.py
similarity index 72%
rename from voltha/adapters/adtran_olt/adtranolt_platform.py
rename to voltha/adapters/adtran_olt/resources/adtranolt_platform.py
index 317d448..22bc8c1 100644
--- a/voltha/adapters/adtran_olt/adtranolt_platform.py
+++ b/voltha/adapters/adtran_olt/resources/adtranolt_platform.py
@@ -18,16 +18,16 @@
import voltha.protos.device_pb2 as dev_pb2
#######################################################################
-##
-## This is a copy of the OpenOLT file of a similar name and is used
-## when running in non-xPON (OpenOLT/SEBA) mode. We need to closely
-## watch for changes in the OpenOLT and eventually work together to
-## have a better way to do things (and more ONUs than 112)
-##
-## TODO: These duplicate some methods in the OLT Handler. Clean up
-## and use a separate file and include it into OLT Handler object
-## as something it derives from.
-##
+#
+# This is a copy of the OpenOLT file of a similar name and is used
+# when running in non-xPON (OpenOLT/SEBA) mode. We need to closely
+# watch for changes in the OpenOLT and eventually work together to
+# have a better way to do things (and more ONUs than 112)
+#
+# TODO: These duplicate some methods in the OLT Handler. Clean up
+# and use a separate file and include it into OLT Handler object
+# as something it derives from.
+#
#######################################################################
"""
Encoding of identifiers
@@ -55,13 +55,13 @@
For Adtran, 1024..1919
Unique per PON interface
- 9 7 6 0
- +-----+------------+
- | idx | onu id | + (Min Alloc ID)
- +-----+------------+
+ 9 8 7 0
+ +-----+----------+
+ | idx | onu_id | + (Min Alloc ID)
+ +-----+----------+
- onu id = 7 bits = 128 ONUs per PON
- Alloc index = 3 bits = 64 GEM ports per ONU
+ onu id = 8 bit
+ Alloc index = 2 bits (max 4 TCONTs/ONU)
Flow id
@@ -117,7 +117,16 @@
MAX_GEM_PORT_ID = MIN_GEM_PORT_ID + 2046
MAX_ONUS_PER_PON = 128
-MAX_TCONTS_PER_ONU = 7
+MAX_TCONTS_PER_ONU = 4
+MAX_GEM_PORTS_PER_ONU = 16 # Hardware can handle more
+
+
+class adtran_platform(object):
+ 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(intf_id, onu_id):
@@ -130,15 +139,6 @@
return intf_id << 11 | onu_id << 4
-# def onu_id_from_uni_port_num(port_num):
-# """
-# Extract the ONU ID from a virtual UNI Port Number
-# :param port_num: (int) virtual UNI / vENET port number on OLT PON
-# :return: (int) onu ID
-# """
-# return (port_num >> 4) & 0x7F
-
-
def intf_id_from_uni_port_num(port_num):
"""
Extract the PON device port number from a virtual UNI Port number
@@ -159,7 +159,7 @@
"""
assert 0 <= onu_id < MAX_ONUS_PER_PON, 'Invalid ONU ID. Expect 0..{}'.format(MAX_ONUS_PER_PON-1)
assert 0 <= idx <= MAX_TCONTS_PER_ONU, 'Invalid TCONT instance. Expect 0..{}'.format(MAX_TCONTS_PER_ONU)
- alloc_id = MIN_TCONT_ALLOC_ID + (onu_id << 3) + idx
+ alloc_id = MIN_TCONT_ALLOC_ID + (idx << 8) + onu_id
return alloc_id
@@ -178,23 +178,6 @@
return MIN_GEM_PORT_ID + (onu_id << 4) + idx
-# def onu_id_from_gemport_id(gemport_id):
-# """
-# Determine ONU ID from a GEM PORT ID. This is only called by the OLT
-#
-# :param gemport_id: (int) GEM Port ID
-# """
-# return (gemport_id - MIN_GEM_PORT_ID) >> 4
-
-
-# def mk_flow_id(intf_id, onu_id, idx):
-# return intf_id << 11 | onu_id << 4 | idx
-
-
-# def intf_id_from_pon_id(port_no):
-# return port_no - 5
-
-
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)
@@ -226,29 +209,6 @@
raise Exception('Invalid intf_id value')
-# def intf_id_to_port_type_name(intf_id):
-# try:
-# return port_type_name_by_port_index(intf_id_to_intf_type(intf_id))
-# except Exception as err:
-# raise Exception(err)
-
-
-# def port_type_name_by_port_index(port_index):
-# try:
-# return dev_pb2._PORT_PORTTYPE.values_by_number[port_index].name
-# except Exception as err:
-# raise Exception(err)
-
-
-# def extract_access_from_flow(in_port, out_port):
-# if is_upstream(in_port, out_port):
-# return (intf_id_from_uni_port_num(in_port), onu_id_from_uni_port_num(
-# in_port))
-# else:
-# return (intf_id_from_uni_port_num(out_port), onu_id_from_uni_port_num(
-# out_port))
-
-
def is_upstream(in_port, out_port):
# FIXME
# if out_port in [128, 129, 130, 131, 0xfffd, 0xfffffffd]:
diff --git a/voltha/adapters/adtran_olt/xpon/adtran_olt_xpon.py b/voltha/adapters/adtran_olt/xpon/adtran_olt_xpon.py
deleted file mode 100644
index 1bfe009..0000000
--- a/voltha/adapters/adtran_olt/xpon/adtran_olt_xpon.py
+++ /dev/null
@@ -1,567 +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 adtran_xpon import AdtranXPON
-from voltha.protos.bbf_fiber_base_pb2 import \
- ChannelgroupConfig, ChannelpartitionConfig, ChannelpairConfig, \
- ChannelterminationConfig, OntaniConfig, VOntaniConfig, VEnetConfig
-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
-from voltha.protos.bbf_fiber_multicast_gemport_body_pb2 import \
- MulticastGemportsConfigData
-from voltha.protos.bbf_fiber_multicast_distribution_set_body_pb2 import \
- MulticastDistributionSetData
-import voltha.adapters.adtran_olt.adtranolt_platform as platform
-
-log = structlog.get_logger()
-
-
-# SEBA
-class AdtranOltXPON(AdtranXPON):
- """
- Class to for OLT and XPON operations
- """
- def __init__(self, **kwargs):
- super(AdtranOltXPON, self).__init__(**kwargs)
-
- # xPON config dictionaries
- self._channel_groups = {} # Name -> dict
- self._channel_partitions = {} # Name -> dict
- self._channel_pairs = {} # Name -> dict
- self._channel_terminations = {} # Name -> dict
-
- @property
- def channel_terminations(self):
- return self._channel_terminations
-
- @property
- def channel_groups(self):
- return self._channel_groups
-
- @property
- def channel_pairs(self):
- return self._channel_pairs
-
- @property
- def channel_partitions(self):
- return self._channel_partitions
-
- # SEBA
- def get_device_profile_info(self, pon, serial_number):
- """ TODO: For now, push into legacy xPON structures. Clean up once we deprecate xPON and remove it"""
- pon_id = pon.pon_id
- if pon_id not in self._cached_xpon_pon_info:
- venets = dict()
- v_ont_anis = dict()
- ont_anis = dict()
- tconts = dict()
- gem_ports = dict()
- groups = {
- "group-{}".format(pon_id):
- {
- 'name' : "group-{}".format(pon_id),
- 'system-id' : '000000',
- 'enabled' : True,
- 'polling-period': 200
- }
- }
- partitions = {
- "partition-{}".format(pon_id):
- {
- 'name' : "partition-{}".format(pon_id),
- 'enabled' : True,
- 'fec-downstream' : True,
- 'mcast-aes' : False,
- 'channel-group' : 'group-{}'.format(pon_id),
- 'authentication-method' : 'serial-number',
- 'differential-fiber-distance': 20
- }
- }
- pairs = {
- 'pon-{}'.format(pon_id):
- {
- 'name' : 'pon-{}.format(pon_id)',
- 'enabled' : True,
- 'channel-group' : 'group-{}'.format(pon_id),
- 'channel-partition': 'partition-{}'.format(pon_id),
- 'line-rate' : 'down_10_up_10',
-
- }
- }
- terminations = {
- 'channel-termination {}'.format(pon_id):
- {
- 'name' : 'channel-termination {}'.format(pon_id),
- 'enabled' : True,
- 'channel-pair' : 'pon-{}'.format(pon_id),
- 'xgpon-ponid' : pon_id,
- 'xgs-ponid' : pon_id,
- 'ber-calc-period': 10,
- }
- }
- # Save this to the cache
- self._cached_xpon_pon_info[pon_id] = {
- 'channel-terminations': terminations,
- 'channel-pairs' : pairs,
- 'channel-partitions' : partitions,
- 'channel-groups' : groups,
- 'vont-anis' : v_ont_anis,
- 'ont-anis' : ont_anis,
- 'v-enets' : venets,
- 'tconts' : tconts,
- 'gem-ports' : gem_ports
- }
- # Now update vont_ani and ont_ani information if needed
- xpon_info = self._cached_xpon_pon_info[pon_id]
-
- vont_ani_info = next((info for _, info in xpon_info['vont-anis'].items()
- if info.get('expected-serial-number') == serial_number), None)
- # New ONU?
- activate_onu = vont_ani_info is None
-
- if activate_onu:
- onu_id = pon.get_next_onu_id
- name = 'customer-000000-{}-{}'.format(pon_id, onu_id)
- vont_ani_info = {
- 'name' : name,
- 'enabled' : True,
- 'description' : '',
- 'onu-id' : onu_id,
- 'expected-serial-number' : serial_number,
- 'expected-registration-id': '', # TODO: How about this?
- 'channel-partition' : 'partition-{}'.format(pon_id),
- 'upstream-channel-speed' : 10000000000,
- 'preferred-channel-pair' : 'pon-{}'.format(pon_id)
- }
- ont_ani_info = {
- 'name': name,
- 'description': '',
- 'enabled': True,
- 'upstream-fec': True,
- 'mgnt-gemport-aes': False
- }
- xpon_info['vont-anis'][name] = vont_ani_info
- xpon_info['ont-anis'][name] = ont_ani_info
-
- 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
-
- tcont = TcontsConfigData()
- tcont.name = 'tcont-{}-{}-data'.format(pon_id, onu_id)
- tcont.alloc_id = platform.mk_alloc_id(pon_id, onu_id)
-
- 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,
- 'vont-ani': name,
- '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
- }
- from olt_tcont import OltTCont
- from olt_traffic_descriptor import OltTrafficDescriptor
- tc['object'] = OltTCont.create(tc, OltTrafficDescriptor.create(tc['td-ref']))
- xpon_info['tconts'][tcont.name] = tc['object']
-
- # gem port creation (this initial one is for untagged ONU data support / EAPOL)
- gem_port, gp = self.create_gem_port(pon_id, onu_id, 0, tcont, untagged=True)
- gem_ports = [gem_port]
-
- from olt_gem_port import OltGemPort
- gp['object'] = OltGemPort.create(self, gp)
- xpon_info['gem-ports'][gem_port.name] = gp['object']
-
- # Now create the User-Data GEM-Ports
- nub_priorities = 1 # TODO: Pull form tech-profile later
- for index in range(1, nub_priorities + 1):
- gem_port, gp = self.create_gem_port(pon_id, onu_id, index, tcont)
- gem_ports.append(gem_port)
-
- from olt_gem_port import OltGemPort
- gp['object'] = OltGemPort.create(self, gp)
- xpon_info['gem-ports'][gem_port.name] = gp['object']
-
- self.create_tcont(tcont, traffic_desc)
- for gem_port in gem_ports:
- self.xpon_create(gem_port)
-
- return xpon_info, activate_onu
-
- def create_gem_port(self, pon_id, onu_id, index, tcont, untagged=False):
- # gem port creation (this initial one is for untagged ONU data support / EAPOL)
- gem_port = GemportsConfigData()
- gem_port.gemport_id = platform.mk_gemport_id(pon_id, onu_id, idx=index)
- if untagged:
- gem_port.name = 'gemport-{}-{}-untagged-{}'.format(pon_id, onu_id, gem_port.gemport_id)
- else:
- gem_port.name = 'gemport-{}-{}-data-{}'.format(pon_id, onu_id, gem_port.gemport_id)
-
- 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
-
- # SEBA
- def get_xpon_info(self, pon_id, pon_id_type='xgs-ponid'):
- """
- Lookup all xPON configuration data for a specific pon-id / channel-termination
- :param pon_id: (int) PON Identifier
- :return: (dict) reduced xPON information for the specific PON port
- """
- if pon_id not in self._cached_xpon_pon_info:
- terminations = {key: val for key, val in self._channel_terminations.iteritems()
- if val[pon_id_type] == pon_id}
-
- pair_names = set([term['channel-pair'] for term in terminations.itervalues()])
- pairs = {key: val for key, val in self.channel_pairs.iteritems()
- if key in pair_names}
-
- partition_names = set([pair['channel-partition'] for pair in pairs.itervalues()])
- partitions = {key: val for key, val in self.channel_partitions.iteritems()
- if key in partition_names}
-
- v_ont_anis = {key: val for key, val in self.v_ont_anis.iteritems()
- if val['preferred-channel-pair'] in pair_names}
- v_ont_ani_names = set(v_ont_anis.keys())
-
- ont_anis = {key: val for key, val in self.ont_anis.iteritems()
- if key in v_ont_ani_names}
-
- group_names = set(pair['channel-group'] for pair in pairs.itervalues())
- groups = {key: val for key, val in self.channel_groups.iteritems()
- if key in group_names}
-
- venets = {key: val for key, val in self.v_enets.iteritems()
- if val['vont-ani'] in v_ont_ani_names}
-
- tconts = {key: val['object'] for key, val in self.tconts.iteritems()
- if val['vont-ani'] in v_ont_ani_names and 'object' in val}
- tcont_names = set(tconts.keys())
-
- gem_ports = {key: val['object'] for key, val in self.gem_ports.iteritems()
- if val['tcont-ref'] in tcont_names and 'object' in val}
-
- self._cached_xpon_pon_info[pon_id] = {
- 'channel-terminations': terminations,
- 'channel-pairs': pairs,
- 'channel-partitions': partitions,
- 'channel-groups': groups,
- 'vont-anis': v_ont_anis,
- 'ont-anis': ont_anis,
- 'v-enets': venets,
- 'tconts': tconts,
- 'gem-ports': gem_ports
- }
- return self._cached_xpon_pon_info[pon_id]
-
- def get_related_pons(self, item, pon_type='xgs-ponid'):
- pon_ids = set()
- ports = []
- data = item['data']
-
- if isinstance(data, ChannelgroupConfig):
- group_name = item['name']
- pair_names = {val['name'] for val in self.channel_pairs.itervalues()
- if val['channel-group'] == group_name}
- pon_ids = {val[pon_type] for val in self.channel_terminations.itervalues()
- if val['channel-pair'] in pair_names}
-
- elif isinstance(data, ChannelpartitionConfig):
- part_name = item['name']
- pair_names = {val['name'] for val in self.channel_pairs.itervalues()
- if val['channel-partition'] == part_name}
- pon_ids = {val[pon_type] for val in self.channel_terminations.itervalues()
- if val['channel-pair'] in pair_names}
-
- elif isinstance(data, ChannelpairConfig):
- pair_name = item['name']
- pon_ids = {val[pon_type] for val in self.channel_terminations.itervalues()
- if val['channel-pair'] == pair_name}
-
- elif isinstance(data, ChannelterminationConfig):
- pon_ids = [item[pon_type]]
-
- elif isinstance(data, (OntaniConfig, VOntaniConfig)):
- # ont_ani name == vont_ani name since no link table support yet
- vont_name = item['name']
- pair_name = self.v_ont_anis[vont_name]['preferred-channel-pair'] \
- if vont_name in self.v_ont_anis else None
-
- if pair_name is not None:
- pon_ids = {val[pon_type] for val in self.channel_terminations.itervalues()
- if val['channel-pair'] == pair_name}
-
- elif isinstance(data, VEnetConfig):
- venet_name = item['name']
- vont_name = self.v_enets[venet_name]['vont-ani'] \
- if venet_name in self.v_enets else None
-
- if vont_name is not None and vont_name in self.v_ont_anis:
- pair_name = self.v_ont_anis[vont_name]['preferred-channel-pair']
- pon_ids = {val[pon_type] for val in self.channel_terminations.itervalues()
- if val['channel-pair'] == pair_name}
-
- elif isinstance(data, TcontsConfigData):
- tcont_name = item['name']
- vont_name = self.tconts[tcont_name]['vont-ani'] \
- if tcont_name in self.tconts else None
-
- if vont_name is not None and vont_name in self.v_ont_anis:
- pair_name = self.v_ont_anis[vont_name]['preferred-channel-pair']
- pon_ids = {val[pon_type] for val in self.channel_terminations.itervalues()
- if val['channel-pair'] == pair_name}
-
- elif isinstance(data, TrafficDescriptorProfileData):
- td_name = item['name']
-
- elif isinstance(data, GemportsConfigData):
- gem_name = item['name']
- venet_name = self.gem_ports[gem_name]['venet-ref'] \
- if gem_name in self.gem_ports else None
-
- vont_name = self.v_enets[venet_name]['vont-ani'] \
- if venet_name in self.v_enets else None
-
- if vont_name is not None and vont_name in self.v_ont_anis:
- pair_name = self.v_ont_anis[vont_name]['preferred-channel-pair']
- pon_ids = {val[pon_type] for val in self.channel_terminations.itervalues()
- if val['channel-pair'] == pair_name}
-
- elif isinstance(data, MulticastGemportsConfigData):
- raise NotImplementedError('TODO')
-
- elif isinstance(data, MulticastDistributionSetData):
- raise NotImplementedError('TODO')
-
- for pon_id in pon_ids:
- pon_port = self.southbound_ports.get(pon_id, None)
- if pon_port is not None:
- ports.append(pon_port)
-
- return ports
-
- def get_related_onus(self, item, pon_type='xgs-ponid'):
- onus = []
- pons = self.get_related_pons(item, pon_type=pon_type)
- data = item['data']
-
- for pon in pons:
- if isinstance(data, (OntaniConfig, VOntaniConfig)):
- # ont_ani name == vont_ani name since no link table support yet
- vont_name = item['name']
- for onu in pon.onus:
- if onu.xpon_name == vont_name:
- onus.append(onu)
-
- elif isinstance(data, VEnetConfig):
- venet_name = item['name']
- vont_name = self.v_enets[venet_name]['vont-ani'] \
- if venet_name in self.v_enets else None
-
- if vont_name is not None and vont_name in self.v_ont_anis:
- for onu in pon.onus:
- if onu.xpon_name == vont_name:
- onus.append(onu)
-
- elif isinstance(data, TcontsConfigData):
- tcont_name = item['name']
- vont_name = self.tconts[tcont_name]['vont-ani'] \
- if tcont_name in self.tconts else None
-
- if vont_name is not None and vont_name in self.v_ont_anis:
- for onu in pon.onus:
- if onu.xpon_name == vont_name:
- onus.append(onu)
-
- elif isinstance(data, TrafficDescriptorProfileData):
- pass
-
- elif isinstance(data, GemportsConfigData):
- gem_name = item['name']
- venet_name = self.gem_ports[gem_name]['venet-ref'] \
- if gem_name in self.gem_ports else None
-
- vont_name = self.v_enets[venet_name]['vont-ani'] \
- if venet_name in self.v_enets else None
-
- if vont_name is not None and vont_name in self.v_ont_anis:
- for onu in pon.onus:
- if onu.xpon_name == vont_name:
- onus.append(onu)
-
- elif isinstance(data, MulticastGemportsConfigData):
- raise NotImplementedError('TODO')
-
- elif isinstance(data, MulticastDistributionSetData):
- raise NotImplementedError('TODO')
-
- return onus
-
- def _get_xpon_collection(self, data):
- collection, create, modify, delete = super(AdtranOltXPON, self)._get_xpon_collection(data)
-
- if collection is not None:
- return collection, create, modify, delete
-
- elif isinstance(data, ChannelgroupConfig):
- return self.channel_groups, \
- self.on_channel_group_create,\
- self.on_channel_group_modify, \
- self.on_channel_group_delete
-
- elif isinstance(data, ChannelpartitionConfig):
- return self.channel_partitions,\
- self.on_channel_partition_create,\
- self.on_channel_partition_modify,\
- self.on_channel_partition_delete
-
- elif isinstance(data, ChannelpairConfig):
- return self.channel_pairs, \
- self.on_channel_pair_create,\
- self.on_channel_pair_modify, \
- self.on_channel_pair_delete
-
- elif isinstance(data, ChannelterminationConfig):
- return self.channel_terminations,\
- self.on_channel_termination_create,\
- self.on_channel_termination_modify,\
- self.on_channel_termination_delete
- return None, None, None, None
-
- def _data_to_dict(self, data, td=None):
- result = super(AdtranOltXPON, self)._data_to_dict(data, td=td)
-
- if result is not None:
- return result
-
- name = data.name
- interface = data.interface
- inst_data = data.data
-
- if isinstance(data, ChannelgroupConfig):
- return 'channel-group', {
- 'name': name,
- 'enabled': interface.enabled,
- 'system-id': inst_data.system_id,
- 'polling-period': inst_data.polling_period,
- 'data': data
- }
-
- elif isinstance(data, ChannelpartitionConfig):
- def _auth_method_enum_to_string(value):
- from voltha.protos.bbf_fiber_types_pb2 import SERIAL_NUMBER, LOID, \
- REGISTRATION_ID, OMCI, DOT1X
- return {
- SERIAL_NUMBER: 'serial-number',
- LOID: 'loid',
- REGISTRATION_ID: 'registration-id',
- OMCI: 'omci',
- DOT1X: 'dot1x'
- }.get(value, 'unknown')
-
- return 'channel-partition', {
- 'name': name,
- 'enabled': interface.enabled,
- 'authentication-method': _auth_method_enum_to_string(inst_data.authentication_method),
- 'channel-group': inst_data.channelgroup_ref,
- 'fec-downstream': inst_data.fec_downstream,
- 'mcast-aes': inst_data.multicast_aes_indicator,
- 'differential-fiber-distance': inst_data.differential_fiber_distance,
- 'data': data
- }
-
- elif isinstance(data, ChannelpairConfig):
- return 'channel-pair', {
- 'name': name,
- 'enabled': interface.enabled,
- 'channel-group': inst_data.channelgroup_ref,
- 'channel-partition': inst_data.channelpartition_ref,
- 'line-rate': inst_data.channelpair_linerate,
- 'data': data
- }
-
- elif isinstance(data, ChannelterminationConfig):
- return 'channel-termination', {
- 'name': name,
- 'enabled': interface.enabled,
- 'xgs-ponid': inst_data.xgs_ponid,
- 'xgpon-ponid': inst_data.xgpon_ponid,
- 'channel-pair': inst_data.channelpair_ref,
- 'ber-calc-period': inst_data.ber_calc_period,
- 'data': data
- }
-
- else:
- raise NotImplementedError('Unknown data type')
-
- def on_channel_group_create(self, cgroup):
- return cgroup # Implement in your OLT, if needed
-
- def on_channel_group_modify(self, cgroup, update, diffs):
- return update # Implement in your OLT, if needed
-
- def on_channel_group_delete(self, cgroup):
- return None # Implement in your OLT, if needed
-
- def on_channel_partition_create(self, cpartition):
- return cpartition # Implement in your OLT, if needed
-
- def on_channel_partition_modify(self, cpartition, update, diffs):
- return update # Implement in your OLT, if needed
-
- def on_channel_partition_delete(self, cpartition):
- return None # Implement in your OLT, if needed
-
- def on_channel_pair_create(self, cpair):
- return cpair # Implement in your OLT, if needed
-
- def on_channel_pair_modify(self, cpair, update, diffs):
- return update # Implement in your OLT, if needed
-
- def on_channel_pair_delete(self, cpair):
- return None # Implement in your OLT, if needed
-
- def on_channel_termination_create(self, cterm):
- return cterm # Implement in your OLT, if needed
-
- def on_channel_termination_modify(self, cterm, update, diffs):
- return update # Implement in your OLT, if needed
-
- def on_channel_termination_delete(self, cterm):
- return None # Implement in your OLT, if needed
diff --git a/voltha/adapters/adtran_olt/xpon/adtran_xpon.py b/voltha/adapters/adtran_olt/xpon/adtran_xpon.py
index 61756d2..30e5919 100644
--- a/voltha/adapters/adtran_olt/xpon/adtran_xpon.py
+++ b/voltha/adapters/adtran_olt/xpon/adtran_xpon.py
@@ -14,20 +14,13 @@
import structlog
from traffic_descriptor import TrafficDescriptor
-from voltha.protos.bbf_fiber_base_pb2 import \
- OntaniConfig, VOntaniConfig, VEnetConfig
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
-from voltha.protos.bbf_fiber_multicast_gemport_body_pb2 import \
- MulticastGemportsConfigData
-from voltha.protos.bbf_fiber_multicast_distribution_set_body_pb2 import \
- MulticastDistributionSetData
log = structlog.get_logger()
-# SEBA
class AdtranXPON(object):
"""
Class to abstract common OLT and ONU xPON operations
@@ -36,25 +29,9 @@
# xPON config dictionaries
self._v_ont_anis = {} # Name -> dict
self._ont_anis = {} # Name -> dict
- self._v_enets = {} # Name -> dict
self._tconts = {} # Name -> dict
self._traffic_descriptors = {} # Name -> dict
self._gem_ports = {} # Name -> dict
- self._mcast_gem_ports = {} # Name -> dict
- self._mcast_dist_sets = {} # Name -> dict
- self._cached_xpon_pon_info = {} # PON-id -> dict
-
- @property
- def v_ont_anis(self):
- return self._v_ont_anis
-
- @property
- def ont_anis(self):
- return self._ont_anis
-
- @property
- def v_enets(self):
- return self._v_enets
@property
def tconts(self):
@@ -71,27 +48,12 @@
def _get_xpon_collection(self, data):
"""
Get the collection for the object type and handler routines
- :param data: xPON object
+ :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, OntaniConfig):
- return self.ont_anis, \
- self.on_ont_ani_create,\
- self.on_ont_ani_modify, \
- self.on_ont_ani_delete
-
- elif isinstance(data, VOntaniConfig):
- return self.v_ont_anis, \
- self.on_vont_ani_create,\
- self.on_vont_ani_modify, \
- self.on_vont_ani_delete
-
- elif isinstance(data, VEnetConfig):
- return self.v_enets, \
- self.on_venet_create,\
- self.on_venet_modify, \
- self.on_venet_delete
-
- elif isinstance(data, TcontsConfigData):
+ if isinstance(data, TcontsConfigData):
return self.tconts, \
self.on_tcont_create,\
self.on_tcont_modify, \
@@ -109,64 +71,10 @@
self.on_gemport_modify, \
self.on_gemport_delete
- elif isinstance(data, MulticastGemportsConfigData):
- return self.mcast_gem_ports, \
- self.on_mcast_gemport_create,\
- self.on_mcast_gemport_modify, \
- self.on_mcast_gemport_delete
-
- elif isinstance(data, MulticastDistributionSetData):
- return self.mcast_dist_sets, \
- self.on_mcast_dist_set_create,\
- self.on_mcast_dist_set_modify, \
- self.on_mcast_dist_set_delete
-
return None, None, None, None
def _data_to_dict(self, data, td=None):
- if isinstance(data, OntaniConfig):
- name = data.name
- interface = data.interface
- inst_data = data.data
-
- return 'ont-ani', {
- 'name': name,
- 'description': interface.description,
- 'enabled': interface.enabled,
- 'upstream-fec': inst_data.upstream_fec_indicator,
- 'mgnt-gemport-aes': inst_data.mgnt_gemport_aes_indicator,
- 'data': data
- }
- elif isinstance(data, VOntaniConfig):
- name = data.name
- interface = data.interface
- inst_data = data.data
-
- return 'vOnt-ani', {
- 'name': name,
- 'description': interface.description,
- 'enabled': interface.enabled,
- 'onu-id': inst_data.onu_id,
- 'expected-serial-number': inst_data.expected_serial_number,
- 'expected-registration-id': inst_data.expected_registration_id,
- 'preferred-channel-pair': inst_data.preferred_chanpair,
- 'channel-partition': inst_data.parent_ref,
- 'upstream-channel-speed': inst_data.upstream_channel_speed,
- 'data': data
- }
- elif isinstance(data, VEnetConfig):
- name = data.name
- interface = data.interface
- inst_data = data.data
-
- return 'vEnet', {
- 'name': name,
- 'description': interface.description,
- 'enabled': interface.enabled,
- 'vont-ani': inst_data.v_ontani_ref,
- 'data': data
- }
- elif isinstance(data, TcontsConfigData):
+ if isinstance(data, TcontsConfigData):
return 'TCONT', {
'name': data.name,
'alloc-id': data.alloc_id,
@@ -198,26 +106,6 @@
'venet-ref': data.itf_ref, # vENET
'data': data
}
- elif isinstance(data, MulticastGemportsConfigData):
- return 'MCAST-GEM', {
- 'name': data.name,
- 'gemport-id': data.gemport_id,
- 'traffic-class': data.traffic_class,
- 'is-broadcast': data.is_broadcast,
- 'channel-pair-ref': data.itf_ref, # channel-pair
- 'data': data
- }
- elif isinstance(data, MulticastDistributionSetData):
- data_dict = {
- 'name': data.name,
- 'multicast-gemport-ref': data.multicast_gemport_ref,
- 'multicast-vlans-all': None,
- 'multicast-vlans-list': [],
- 'data': data
- }
- assert True is False, 'Need to decode multicast-vlans parameter'
- return 'MCAST-Distribution', data_dict
-
return None
def create_tcont(self, tcont_data, td_data):
@@ -307,7 +195,6 @@
log.debug('new-item', item_type=item_type, item=new_item)
items[name] = new_item
- self._cached_xpon_pon_info = {} # Clear cached data
if create_method is not None:
try:
@@ -322,9 +209,6 @@
def xpon_update(self, data, td=None):
log.debug('xpon-update', data=data)
- if not self.xpon_support:
- raise NotImplementedError("xPON support has been disabled")
-
name = data.name
items, create, update_method, delete = self._get_xpon_collection(data)
@@ -361,7 +245,6 @@
return
items[name] = update_item
- self._cached_xpon_pon_info = {} # Clear cached data
# Act on any changed items
if update_method is not None:
@@ -379,87 +262,7 @@
def xpon_remove(self, data):
log.debug('xpon_remove', data=data)
- if not self.xpon_support:
- raise NotImplementedError("xPON support has been disabled")
-
- name = data.name
-
- items, create, update, delete_method = self._get_xpon_collection(data)
- item = items.get(name)
-
- if item is not None:
- if delete_method is None:
- item = None
- else:
- try:
- item = delete_method(item)
-
- except Exception as e:
- log.exception('xpon-remove', item=items, e=e)
-
- self._cached_xpon_pon_info = {} # Clear cached data
-
- if item is None:
- del items[name]
- else:
- # Update item in collection (still referenced somewhere)
- items[name] = item
-
- def on_ont_ani_create(self, ont_ani):
- """
- A new ONT-ani is being created. You can override this method to
- perform custom operations as needed. If you override this method, you can add
- additional items to the item dictionary to track additional implementation
- key/value pairs.
-
- :param ont_ani: (dict) new ONT-ani
- :return: (dict) Updated ONT-ani dictionary, None if item should be deleted
- """
- return ont_ani # Implement in your OLT, if needed
-
- def on_ont_ani_modify(self, ont_ani, update, diffs):
- """
- A existing ONT-ani is being updated. You can override this method to
- perform custom operations as needed. If you override this method, you can add
- additional items to the item dictionary to track additional implementation
- key/value pairs.
-
- :param ont_ani: (dict) existing ONT-ani item dictionary
- :param update: (dict) updated (changed) ONT-ani
- :param diffs: (dict) collection of items different in the update
- :return: (dict) Updated ONT-ani dictionary, None if item should be deleted
- """
- return update # Implement in your OLT, if needed
-
- def on_ont_ani_delete(self, ont_ani):
- """
- A existing ONT-ani is being deleted. You can override this method to
- perform custom operations as needed. If you override this method, you can add
- additional items to the item dictionary to track additional implementation
- key/value pairs.
-
- :param ont_ani: (dict) ONT-ani to delete
- :return: (dict) None if item should be deleted
- """
- return None # Implement in your OLT, if needed
-
- def on_vont_ani_create(self, vont_ani):
- return vont_ani # Implement in your OLT, if needed
-
- def on_vont_ani_modify(self, vont_ani, update, diffs):
- return update # Implement in your OLT, if needed
-
- def on_vont_ani_delete(self, vont_ani):
- return None # Implement in your OLT, if needed
-
- def on_venet_create(self, venet):
- return venet # Implement in your OLT, if needed
-
- def on_venet_modify(self, venet, update, diffs):
- return update # Implement in your OLT, if needed
-
- def on_venet_delete(self, venet):
- return None # Implement in your OLT, if needed
+ raise NotImplementedError("xPON support has been disabled")
def on_tcont_create(self, tcont):
return tcont # Implement in your OLT, if needed
@@ -487,21 +290,3 @@
def on_gemport_delete(self, gem_port):
return None # Implement in your OLT, if needed
-
- def on_mcast_gemport_create(self, mcast_gem_port):
- return mcast_gem_port # Implement in your OLT, if needed
-
- def on_mcast_gemport_modify(self, mcast_gem_port, update, diffs):
- return update # Implement in your OLT, if needed
-
- def on_mcast_gemport_delete(self, mcast_gem_port):
- return None # Implement in your OLT, if needed
-
- def on_mcast_dist_set_create(self, dist_set):
- return dist_set # Implement in your OLT, if needed
-
- def on_mcast_dist_set_modify(self, dist_set, update, diffs):
- return update # Implement in your OLT, if needed
-
- def on_mcast_dist_set_delete(self, dist_set):
- 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 5cc84b2..50149b5 100644
--- a/voltha/adapters/adtran_olt/xpon/gem_port.py
+++ b/voltha/adapters/adtran_olt/xpon/gem_port.py
@@ -23,20 +23,16 @@
multicast=False,
tcont_ref=None,
traffic_class=None,
- intf_ref=None,
- untagged=False,
name=None,
handler=None):
self.name = name
self.gem_id = gem_id
self._alloc_id = alloc_id
self.tcont_ref = tcont_ref
- self.intf_ref = intf_ref
self.traffic_class = traffic_class
self._encryption = encryption
self._omci_transport = omci_transport
self.multicast = multicast
- self.untagged = untagged
self._handler = handler
# TODO: Make this a base class and derive OLT and ONU specific classes from it
diff --git a/voltha/adapters/adtran_olt/xpon/olt_gem_port.py b/voltha/adapters/adtran_olt/xpon/olt_gem_port.py
index c50a858..6e37474 100644
--- a/voltha/adapters/adtran_olt/xpon/olt_gem_port.py
+++ b/voltha/adapters/adtran_olt/xpon/olt_gem_port.py
@@ -26,14 +26,12 @@
"""
Adtran OLT specific implementation
"""
- def __init__(self, gem_id, alloc_id,
+ def __init__(self, gem_id, alloc_id, pon_id, onu_id,
encryption=False,
omci_transport=False,
multicast=False,
tcont_ref=None,
traffic_class=None,
- intf_ref=None,
- untagged=False,
name=None,
handler=None,
is_mock=False,
@@ -44,32 +42,27 @@
multicast=multicast,
tcont_ref=tcont_ref,
traffic_class=traffic_class,
- intf_ref=intf_ref,
- untagged=untagged,
name=name,
handler=handler)
self._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
@staticmethod
- def create(handler, gem_port):
- mcast = gem_port['gemport-id'] in [4095] # TODO: Perform proper lookup
- untagged = 'untagged' in gem_port['name'].lower()
- # TODO: Use next once real BBF mcast available.
- # port_ref = 'channel-pair-ref 'if mcast else 'venet-ref'
- port_ref = 'venet-ref 'if mcast else 'venet-ref'
+ def create(handler, gem_port, pon_id, onu_id):
+ mcast = False # gem_port['gemport-id'] in [4095] # TODO: Perform proper lookup
return OltGemPort(gem_port['gemport-id'],
None,
+ 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'],
- intf_ref=gem_port.get(port_ref),
handler=handler,
multicast=mcast,
- untagged=untagged,
pb_data=gem_port['data'])
@property
@@ -93,33 +86,30 @@
self.set_config(self._handler.rest_client, 'encryption', value)
@inlineCallbacks
- def add_to_hardware(self, session, pon_id, onu_id, operation='POST'):
+ def add_to_hardware(self, session, operation='POST'):
from ..adtran_olt_handler import AdtranOltHandler
- log.info('add-gem-port-2-hw', pon_id=pon_id, onu_id=onu_id,
- operation=operation, gem_port=self)
- uri = AdtranOltHandler.GPON_GEM_CONFIG_LIST_URI.format(pon_id, onu_id)
+
+ uri = AdtranOltHandler.GPON_GEM_CONFIG_LIST_URI.format(self.pon_id, self.onu_id)
data = json.dumps(self.to_dict())
- name = 'gem-port-create-{}-{}: {}/{}'.format(pon_id, onu_id,
+ name = 'gem-port-create-{}-{}: {}/{}'.format(self.pon_id, self.onu_id,
self.gem_id,
self.alloc_id)
try:
results = yield session.request(operation, uri, data=data, name=name)
+ returnValue(results)
except Exception as e:
if operation == 'POST':
- returnValue(self.add_to_hardware(session, pon_id, onu_id,
- operation='PATCH'))
+ returnValue(self.add_to_hardware(session, operation='PATCH'))
else:
log.exception('add-2-hw', gem=self, e=e)
raise
- returnValue(results)
-
- def remove_from_hardware(self, session, pon_id, onu_id):
+ def remove_from_hardware(self, session):
from ..adtran_olt_handler import AdtranOltHandler
- uri = AdtranOltHandler.GPON_GEM_CONFIG_URI.format(pon_id, onu_id, self.gem_id)
- name = 'gem-port-delete-{}-{}: {}'.format(pon_id, onu_id, self.gem_id)
+ 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)
return session.request('DELETE', uri, name=name)
def set_config(self, session, leaf, value):
diff --git a/voltha/adapters/adtran_olt/xpon/olt_tcont.py b/voltha/adapters/adtran_olt/xpon/olt_tcont.py
index a409026..804c8c2 100644
--- a/voltha/adapters/adtran_olt/xpon/olt_tcont.py
+++ b/voltha/adapters/adtran_olt/xpon/olt_tcont.py
@@ -24,37 +24,35 @@
"""
Adtran OLT specific implementation
"""
- def __init__(self, alloc_id, traffic_descriptor,
- name=None, vont_ani=None, is_mock=False,
- pb_data = None):
- super(OltTCont, self).__init__(alloc_id, traffic_descriptor,
- name=name, vont_ani=vont_ani)
+ 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
+ self.pon_id = pon_id
+ self.onu_id = onu_id
self.data = pb_data # Needed for non-xPON mode
@staticmethod
- def create(tcont, td):
+ def create(tcont, td, pon_id, onu_id):
from olt_traffic_descriptor import OltTrafficDescriptor
assert isinstance(tcont, dict), 'TCONT should be a dictionary'
assert isinstance(td, OltTrafficDescriptor), 'Invalid Traffic Descriptor data type'
- return OltTCont(tcont['alloc-id'], td,
+ return OltTCont(tcont['alloc-id'], td, pon_id, onu_id,
name=tcont['name'],
- vont_ani=tcont['vont-ani'],
pb_data=tcont['data'])
@inlineCallbacks
- def add_to_hardware(self, session, pon_id, onu_id):
+ def add_to_hardware(self, session):
+ from ..adtran_olt_handler import AdtranOltHandler
if self._is_mock:
returnValue('mock')
- from ..adtran_olt_handler import AdtranOltHandler
- log.info('add-tcont-2-hw', pon_id=pon_id, onu_id=onu_id, tcont=self)
-
- uri = AdtranOltHandler.GPON_TCONT_CONFIG_LIST_URI.format(pon_id, onu_id)
+ uri = AdtranOltHandler.GPON_TCONT_CONFIG_LIST_URI.format(self.pon_id, self.onu_id)
data = json.dumps({'alloc-id': self.alloc_id})
- name = 'tcont-create-{}-{}: {}'.format(pon_id, onu_id, self.alloc_id)
+ name = 'tcont-create-{}-{}: {}'.format(self.pon_id, self.onu_id, self.alloc_id)
# For TCONT, only leaf is the key. So only post needed
try:
@@ -66,7 +64,7 @@
if self.traffic_descriptor is not None:
try:
results = yield self.traffic_descriptor.add_to_hardware(session,
- pon_id, onu_id,
+ self.pon_id, self.onu_id,
self.alloc_id)
except Exception as e:
log.exception('traffic-descriptor', tcont=self,
@@ -75,11 +73,14 @@
returnValue(results)
- def remove_from_hardware(self, session, pon_id, onu_id):
+ def remove_from_hardware(self, session):
from ..adtran_olt_handler import AdtranOltHandler
+ 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)
- name = 'tcont-delete-{}-{}: {}'.format(pon_id, 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/tcont.py b/voltha/adapters/adtran_olt/xpon/tcont.py
index 5f2d810..fd65adf 100644
--- a/voltha/adapters/adtran_olt/xpon/tcont.py
+++ b/voltha/adapters/adtran_olt/xpon/tcont.py
@@ -17,11 +17,10 @@
"""
Class to wrap TCont capabilities
"""
- def __init__(self, alloc_id, traffic_descriptor, name=None, vont_ani=None):
+ def __init__(self, alloc_id, traffic_descriptor, name=None):
self.alloc_id = alloc_id
self.traffic_descriptor = traffic_descriptor
self.name = name
- self.vont_ani = vont_ani # (string) reference
# 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
diff --git a/voltha/adapters/adtran_onu/adtran_onu.py b/voltha/adapters/adtran_onu/adtran_onu.py
index 53addc2..b573137 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.19',
+ version='1.20',
device_type='adtran_onu',
vendor_id='ADTN',
accepts_add_remove_flow_updates=False), # TODO: Support flow-mods
@@ -147,7 +147,7 @@
handler.receive_message(msg)
######################################################################
- # PON Mgnt APIs
+ # PON Mgnt APIs (Eventually will be deprecated)
def create_interface(self, device, data):
"""
@@ -259,76 +259,19 @@
handler.xpon_remove(data)
def create_multicast_gemport(self, device, data):
- """
- API to create multicast gemport object in the devices
- :param device: device id
- :param data: multicast gemport data object
- :return: None
- """
- self.log.info('create-mcast-gemport', data=data)
- handler = self.devices_handlers.get(device.id)
- if handler is not None:
- handler.xpon_create(data)
+ raise NotImplemented('xPON has been deprecated')
def update_multicast_gemport(self, device, data):
- """
- API to update multicast gemport object in the devices
- :param device: device id
- :param data: multicast gemport data object
- :return: None
- """
- self.log.info('update-mcast-gemport', data=data)
- handler = self.devices_handlers.get(device.id)
- if handler is not None:
- handler.xpon_update(data)
+ raise NotImplemented('xPON has been deprecated')
def remove_multicast_gemport(self, device, data):
- """
- API to delete multicast gemport object in the devices
- :param device: device id
- :param data: multicast gemport data object
- :return: None
- """
- self.log.info('remove-mcast-gemport', data=data)
- handler = self.devices_handlers.get(device.id)
- if handler is not None:
- handler.xpon_remove(data)
+ raise NotImplemented('xPON has been deprecated')
def create_multicast_distribution_set(self, device, data):
- """
- API to create multicast distribution rule to specify
- the multicast VLANs that ride on the multicast gemport
- :param device: device id
- :param data: multicast distribution data object
- :return: None
- """
- self.log.info('create-mcast-distribution-set', data=data)
- handler = self.devices_handlers.get(device.id)
- if handler is not None:
- handler.xpon_create(data)
+ raise NotImplemented('xPON has been deprecated')
def update_multicast_distribution_set(self, device, data):
- """
- API to update multicast distribution rule to specify
- the multicast VLANs that ride on the multicast gemport
- :param device: device id
- :param data: multicast distribution data object
- :return: None
- """
- self.log.info('update-mcast-distribution-set', data=data)
- handler = self.devices_handlers.get(device.id)
- if handler is not None:
- handler.xpon_update(data)
+ raise NotImplemented('xPON has been deprecated')
def remove_multicast_distribution_set(self, device, data):
- """
- API to delete multicast distribution rule to specify
- the multicast VLANs that ride on the multicast gemport
- :param device: device id
- :param data: multicast distribution data object
- :return: None
- """
- self.log.info('remove-mcast-distribution-set', data=data)
- handler = self.devices_handlers.get(device.id)
- if handler is not None:
- handler.xpon_remove(data)
+ raise NotImplemented('xPON has been deprecated')
\ No newline at end of file
diff --git a/voltha/adapters/adtran_onu/adtran_onu_handler.py b/voltha/adapters/adtran_onu/adtran_onu_handler.py
index 4d3b5db..045c559 100644
--- a/voltha/adapters/adtran_onu/adtran_onu_handler.py
+++ b/voltha/adapters/adtran_onu/adtran_onu_handler.py
@@ -33,7 +33,7 @@
from common.utils.indexpool import IndexPool
from voltha.extensions.omci.omci_me import *
-import voltha.adapters.adtran_olt.adtranolt_platform as platform
+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
@@ -54,7 +54,6 @@
self.log = structlog.get_logger(device_id=device_id)
self.logical_device_id = None
self.proxy_address = None
- self._event_messages = None
self._enabled = False
self.pm_metrics = None
self.alarms = None
@@ -63,7 +62,7 @@
self._openomci = OMCI(self, adapter.omci_agent)
self._in_sync_subscription = None
- # TODO: Need to find a way to sync with OLT. It is part of OpenFlow Port number as well
+
self._onu_port_number = 0
self._pon_port_number = 1
self._port_number_pool = IndexPool(_MAXIMUM_PORT, 0)
@@ -71,9 +70,7 @@
self._unis = dict() # Port # -> UniPort
self._pon = PonPort.create(self, self._pon_port_number)
self._heartbeat = HeartBeat.create(self, device_id)
-
self._deferred = None
- self._event_deferred = None
# Flow entries
self._flows = dict()
@@ -84,22 +81,16 @@
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
- # Assume no XPON support unless we get an vont-ani/ont-ani/venet create
- self.xpon_support = False # xPON no longer available
-
def __str__(self):
return "AdtranOnuHandler: {}".format(self.device_id)
def _cancel_deferred(self):
- d1, self._deferred = self._deferred, None
- d2, self._event_deferred = self._event_deferred, None
-
- for d in [d1, d2]:
- try:
- if d is not None and not d.called:
- d.cancel()
- except:
- pass
+ d, self._deferred = self._deferred, None
+ try:
+ if d is not None and not d.called:
+ d.cancel()
+ except:
+ pass
@property
def enabled(self):
@@ -171,15 +162,10 @@
def start(self):
assert self._enabled, 'Start should only be called if enabled'
-
self._cancel_deferred()
- # Handle received ONU event messages TODO: Deprecate this....
- self._event_messages = DeferredQueue()
- self._event_deferred = reactor.callLater(0, self._handle_onu_events)
-
# Register for adapter messages
- self.adapter_agent.register_for_inter_adapter_messages()
+ #self.adapter_agent.register_for_inter_adapter_messages()
# OpenOMCI Startup
self._subscribe_to_events()
@@ -201,7 +187,7 @@
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.stop()
@@ -217,11 +203,6 @@
if self._pon is not None:
self._pon.enabled = False
- queue, self._event_deferred = self._event_deferred, None
- if queue is not None:
- while queue.pending:
- _ = yield queue.get()
-
def receive_message(self, msg):
if self.enabled:
# TODO: Have OpenOMCI actually receive the messages
@@ -252,23 +233,21 @@
self.adapter_agent.add_port(device.id, self._pon.get_port())
def xpon_not_found():
- if not self.xpon_support:
- # Start things up for this ONU Handler.
- self.enabled = True
+ self.enabled = True
# Schedule xPON 'not found' startup for 10 seconds from now. We will
# easily get a vONT-ANI create within that time if xPON is being used
# as this is how we are initially launched and activated in the first
# place if xPON is in use.
- reactor.callLater(10, xpon_not_found)
+ reactor.callLater(10, xpon_not_found) # TODO: Clean up old xPON delay
# reference of uni_port is required when re-enabling the device if
# it was disabled previously
# Need to query ONU for number of supported uni ports
# For now, temporarily set number of ports to 1 - port #2
parent_device = self.adapter_agent.get_device(device.parent_id)
+
self.logical_device_id = parent_device.parent_id
- assert self.logical_device_id, 'Invalid logical device ID'
self.adapter_agent.update_device(device)
############################################################################
@@ -290,7 +269,8 @@
############################################################################
# Setup Alarm handler
self.alarms = AdapterAlarms(self.adapter_agent, device.id, self.logical_device_id)
-
+ self.openomci.onu_omci_device.alarm_synchronizer.set_alarm_params(mgr=self.alarms,
+ ani_ports=[self._pon])
############################################################################
# Start collecting stats from the device after a brief pause
reactor.callLater(30, self.pm_metrics.start_collector)
@@ -316,7 +296,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
@@ -364,7 +344,6 @@
# Ignore untagged upstream etherType flows. These are trapped at the
# OLT and the default flows during initial OMCI service download will
# send them to the Default VLAN (4091) port for us
- #
if is_upstream and flow_entry.vlan_vid is None and flow_entry.etype is not None:
continue
@@ -372,21 +351,11 @@
# since that is already installed and any user-data flows for upstream
# priority tag data will be at a higher level. Also should ignore the
# corresponding priority-tagged to priority-tagged flow as well.
-
if (flow_entry.vlan_vid == 0 and flow_entry.set_vlan_vid == 0) or \
(flow_entry.vlan_vid is None and flow_entry.set_vlan_vid == 0
and not is_upstream):
continue
- # Is it the first user-data flow downstream with a non-zero/non-None VID
- # to match on? If so, use as the device VLAN
- # TODO: When multicast is supported, skip the multicast VLAN here?
-
- if not is_upstream and flow_entry.vlan_vid:
- uni = self.uni_port(flow_entry.out_port)
- if uni is not None:
- uni.subscriber_vlan = flow_entry.vlan_vid
-
# Add it to hardware
try:
def failed(_reason, fid):
@@ -423,13 +392,14 @@
self.log.info('rebooting', device_id=self.device_id)
self._cancel_deferred()
- reregister = True
- try:
- # Drop registration for adapter messages
- self.adapter_agent.unregister_for_inter_adapter_messages()
-
- except KeyError:
- reregister = False
+ 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
@@ -456,7 +426,6 @@
# Reboot in progress. A reboot may take up to 3 min 30 seconds
# Go ahead and pause less than that and start to look
# for it being alive
-
device.reason = 'reboot in progress'
self.adapter_agent.update_device(device)
@@ -471,19 +440,17 @@
@inlineCallbacks
def _finish_reboot(self, previous_oper_status, previous_conn_status,
reregister):
-
# Restart OpenOMCI
self.omci.enabled = True
device = self.adapter_agent.get_device(self.device_id)
-
device.oper_status = previous_oper_status
device.connect_status = previous_conn_status
device.reason = ''
self.adapter_agent.update_device(device)
- if reregister:
- self.adapter_agent.register_for_inter_adapter_messages()
+ # if reregister:
+ # self.adapter_agent.register_for_inter_adapter_messages()
self.log.info('reboot-complete', device_id=self.device_id)
@@ -543,9 +510,7 @@
self.adapter_agent.unregister_for_proxied_messages(device.proxy_address)
# TODO:
- # 1) Remove all flows from the device
- # 2) Remove the device from ponsim
-
+ # 1) Remove all flows from the device? or is it done before we are called
self.log.info('disabled', device_id=device.id)
def reenable(self):
@@ -585,7 +550,7 @@
# reestablish logical ports for each UNI
for uni in self.uni_ports:
self.adapter_agent.add_port(device.id, uni.get_port())
- uni.add_logical_port(uni.logical_port_number, subscriber_vlan=uni.subscriber_vlan)
+ uni.add_logical_port(uni.logical_port_number)
device = self.adapter_agent.get_device(device.id)
device.oper_status = OperStatus.ACTIVE
@@ -603,7 +568,7 @@
def delete(self):
self.log.info('deleting', device_id=self.device_id)
- for uni in self._unis.itervalues():
+ for uni in self._unis.values():
uni.stop()
uni.delete()
@@ -613,208 +578,31 @@
# OpenOMCI cleanup
self._openomci.delete()
- def on_ont_ani_create(self, ont_ani):
- """
- A new ONT-ani is being created. You can override this method to
- perform custom operations as needed. If you override this method, you can add
- additional items to the item dictionary to track additional implementation
- key/value pairs.
-
- :param ont_ani: (dict) new ONT-ani
- :return: (dict) Updated ONT-ani dictionary, None if item should be deleted
- """
- self.xpon_support = True
-
- self.log.info('ont-ani-create', ont_ani=ont_ani)
- self.enabled = ont_ani['enabled']
-
- return ont_ani # Implement in your OLT, if needed
-
- def on_ont_ani_modify(self, ont_ani, update, diffs):
- """
- A existing ONT-ani is being updated. You can override this method to
- perform custom operations as needed. If you override this method, you can add
- additional items to the item dictionary to track additional implementation
- key/value pairs.
-
- :param ont_ani: (dict) existing ONT-ani item dictionary
- :param update: (dict) updated (changed) ONT-ani
- :param diffs: (dict) collection of items different in the update
- :return: (dict) Updated ONT-ani dictionary, None if item should be deleted
- """
- if not self.xpon_support:
- return
-
- valid_keys = ['enabled', 'mgnt-gemport-aes'] # 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("ont_ani leaf '{}' is read-only or write-once".format(invalid_key))
-
- keys = [k for k in diffs.keys() if k in valid_keys]
-
- for k in keys:
- if k == 'enabled':
- self.enabled = update[k]
-
- elif k == 'mgnt-gemport-aes':
- self.mgmt_gemport_aes = update[k]
-
- return update
-
- def on_ont_ani_delete(self, ont_ani):
- """
- A existing ONT-ani is being deleted. You can override this method to
- perform custom operations as needed. If you override this method, you can add
- additional items to the item dictionary to track additional implementation
- key/value pairs.
-
- :param ont_ani: (dict) ONT-ani to delete
- :return: (dict) None if item should be deleted
- """
- if not self.xpon_support:
- return
-
- # TODO: Is this ever called or is the iAdapter 'delete' called first?
- return None # Implement in your OLT, if needed
-
- def on_vont_ani_create(self, vont_ani):
- self.xpon_support = True
- self.log.info('vont-ani-create', vont_ani=vont_ani)
- # TODO: look up PON port and update 'upstream-channel-speed'
- return vont_ani # Implement in your OLT, if needed
-
- def on_vont_ani_modify(self, vont_ani, update, diffs):
- if not self.xpon_support:
- return
-
- valid_keys = ['upstream-channel-speed'] # 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("vont_ani leaf '{}' is read-only or write-once".format(invalid_key))
-
- keys = [k for k in diffs.keys() if k in valid_keys]
-
- for k in keys:
- if k == 'upstream-channel-speed':
- self.upstream_channel_speed = update[k]
-
- return update
-
- def on_vont_ani_delete(self, vont_ani):
- if not self.xpon_support:
- return
-
- return self.delete()
-
- def on_venet_create(self, venet):
- self.xpon_support = True
-
- self.log.info('venet-create', venet=venet)
-
- # TODO: This first set is copied over from BroadCOM ONU. For testing, actual work
- # is the last 7 lines. The 'test' code below assumes we have not registered
- # any UNI ports during 'activate' but we want to create them as the vEnet
- # information comes in.
- # onu_device = self.adapter_agent.get_device(self.device_id)
- # existing_uni_ports = self.adapter_agent.get_ports(onu_device.parent_id, Port.ETHERNET_UNI)
- #
- # parent_port_num = None
- # for uni in existing_uni_ports:
- # if uni.label == venet['name']: # TODO: was -> data.interface.name:
- # parent_port_num = uni.port_no
- # break
- #
- # # Create both the physical and logical ports for the UNI now
- # parent_device = self.adapter_agent.get_device(onu_device.parent_id)
- # logical_device_id = parent_device.parent_id
- # assert logical_device_id, 'Invalid logical device ID'
- # # self.add_uni_port(onu_device, logical_device_id, venet['name'], parent_port_num)
- #
- # pon_ports = self.adapter_agent.get_ports(self.device_id, Port.PON_ONU)
- # if pon_ports:
- # # TODO: Assumed only one PON port and UNI port per ONU.
- # pon_port = pon_ports[0]
- # else:
- # self.log.error("No-Pon-port-configured-yet")
- # return
- #
- # self.adapter_agent.delete_port_reference_from_parent(self.device_id, pon_port)
- # pon_port.peers[0].device_id = onu_device.parent_id
- # pon_port.peers[0].port_no = parent_port_num
- # self.adapter_agent.add_port_reference_to_parent(self.device_id, pon_port)
-
- #################################################################################
- # Start of actual work (what actually does something)
- # TODO: Clean this up. Use looked up UNI
-
- # vlan non-zero if created via legacy method (not xPON). Also
- # Set a random serial number since not xPON based
-
- ofp_port_no, subscriber_vlan, untagged_vlan = UniPort.decode_venet(venet)
-
- self._add_uni_port(self, venet['name'], ofp_port_no, subscriber_vlan,
- untagged_vlan, venet['enabled'])
- return venet
-
- # SEBA - Below is used by xPON mode
- def _add_uni_port(self, port_name, ofp_port_no, subscriber_vlan, untagged_vlan, enable):
- uni_port = UniPort.create(self, port_name,
- self._onu_port_number, # TODO: self._next_port_number,
- ofp_port_no,
- subscriber_vlan,
- untagged_vlan)
-
- device = self.adapter_agent.get_device(self.device_id)
- self.adapter_agent.add_port(device.id, uni_port.get_port())
-
- self._unis[uni_port.port_number] = uni_port
-
- # If the PON has already synchronized, add the logical port now
- # since we know we have been activated
- if self._pon is not None and self.openomci.connected:
- uni_port.add_logical_port(ofp_port_no, subscriber_vlan=subscriber_vlan)
-
- # TODO: Next is just for debugging to see what this call returns after
- # we add a UNI
- # existing_uni_ports = self.adapter_agent.get_ports(onu_device.parent_id, Port.ETHERNET_UNI)
-
- uni_port.enabled = enable
-
def add_uni_ports(self):
""" Called after in-sync achieved and not in xPON mode"""
+ # TODO: We have to methods adding UNI ports. Go to one
# TODO: Should this be moved to the omci.py module for this ONU?
# This is only for working WITHOUT xPON
- assert not self.xpon_support
pptp_entities = self.openomci.onu_omci_device.configuration.pptp_entities
-
device = self.adapter_agent.get_device(self.device_id)
- subscriber_vlan = device.vlan
- untagged_vlan = OMCI.DEFAULT_UNTAGGED_VLAN
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_name = "uni-{}".format(uni_no)
-
mac_bridge_port_num = working_port + 1
-
self.log.debug('live-port-number-ready', uni_no=uni_no, uni_name=uni_name)
- uni_port = UniPort.create(self, uni_name, uni_no, uni_name,
- subscriber_vlan, untagged_vlan)
-
+ 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, subscriber_vlan=subscriber_vlan)
+ uni_port.add_logical_port(uni_port.port_number)
self.log.debug("created-uni-port", uni=uni_port)
@@ -828,7 +616,8 @@
parent_adapter_agent.add_port(device.parent_id, uni_port.get_port())
self._unis[uni_port.port_number] = uni_port
-
+ self.openomci.onu_omci_device.alarm_synchronizer.set_alarm_params(onu_id=self.proxy_address.onu_id,
+ uni_ports=self._unis.values())
# TODO: this should be in the PonPort class
pon_port = self._pon.get_port()
self.adapter_agent.delete_port_reference_from_parent(self.device_id,
@@ -842,46 +631,9 @@
self.adapter_agent.add_port_reference_to_parent(self.device_id,
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_venet_modify(self, venet, update, diffs):
- if not self.xpon_support:
- return
-
- # Look up the associated UNI port
- uni_port = self.uni_port(venet['name'])
-
- if uni_port is not None:
- valid_keys = ['enabled'] # 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("venet leaf '{}' is read-only or write-once".format(invalid_key))
-
- keys = [k for k in diffs.keys() if k in valid_keys]
-
- for k in keys:
- if k == 'enabled':
- uni_port.enabled = update[k]
-
- return update
-
- def on_venet_delete(self, venet):
- if not self.xpon_support:
- return
-
- # Look up the associated UNI port
- uni_port = self.uni_port(venet['name'])
-
- if uni_port is not None:
- port_no = uni_port.port_number
- del self._unis[port_no]
- uni_port.delete()
- self._release_port_number(port_no)
-
- return None
-
def on_tcont_create(self, tcont):
from onu_tcont import OnuTCont
@@ -1003,80 +755,8 @@
return None
- def on_mcast_gemport_create(self, mcast_gem_port):
- return mcast_gem_port # Implement in your OLT, if needed
-
- def on_mcast_gemport_modify(self, mcast_gem_port, update, diffs):
- return mcast_gem_port # Implement in your OLT, if needed
-
- def on_mcast_gemport_delete(self, mcast_gem_port):
- return None # Implement in your OLT, if needed
-
- def on_mcast_dist_set_create(self, dist_set):
- return dist_set # Implement in your OLT, if needed
-
- def on_mcast_dist_set_modify(self, dist_set, update, diffs):
- return update # Implement in your OLT, if needed
-
- def on_mcast_dist_set_delete(self, dist_set):
- return None # Implement in your OLT, if needed
-
def rx_inter_adapter_message(self, msg):
- if self.enabled and self._event_messages is not None:
- self._event_messages.put(msg)
-
- @inlineCallbacks
- def _handle_onu_events(self):
- #
- # TODO: From broadcom ONU. This is from the 'receive_inter_adapter_message()'
- # method.
- #
- event_msg = yield self._event_messages.get()
-
- if self._event_deferred is None:
- returnValue('cancelled')
-
- if event_msg['event'] == 'activation-completed':
- # if event_msg['event_data']['activation_successful']:
- # for uni in self.uni_ports:
- # port_no = self.proxy_address.channel_id + uni
- # reactor.callLater(1,
- # self.message_exchange,
- # self.proxy_address.onu_id,
- # self.proxy_address.onu_session_id,
- # port_no)
- #
- # device = self.adapter_agent.get_device(self.device_id)
- # device.oper_status = OperStatus.ACTIVE
- # self.adapter_agent.update_device(device)
- #
- # else:
- # device = self.adapter_agent.get_device(self.device_id)
- # device.oper_status = OperStatus.FAILED
- # self.adapter_agent.update_device(device)
- pass
-
- elif event_msg['event'] == 'deactivation-completed':
- # device = self.adapter_agent.get_device(self.device_id)
- # device.oper_status = OperStatus.DISCOVERED
- # self.adapter_agent.update_device(device)
- pass
-
- elif event_msg['event'] == 'ranging-completed':
- # if event_msg['event_data']['ranging_successful']:
- # device = self.adapter_agent.get_device(self.device_id)
- # device.oper_status = OperStatus.ACTIVATING
- # self.adapter_agent.update_device(device)
- #
- # else:
- # device = self.adapter_agent.get_device(self.device_id)
- # device.oper_status = OperStatus.FAILED
- # self.adapter_agent.update_device(device)
- pass
-
- # Handle next event (self._event_deferred is None if we got stopped)
-
- self._event_deferred = reactor.callLater(0, self.handle_onu_events)
+ raise NotImplemented('Not currently supported')
def _subscribe_to_events(self):
from voltha.extensions.omci.onu_device_entry import OnuDeviceEvents, \
@@ -1097,7 +777,6 @@
def in_sync_handler(self, _topic, msg):
# Create UNI Ports on first In-Sync event
-
if self._in_sync_subscription is not None:
try:
from voltha.extensions.omci.onu_device_entry import IN_SYNC_KEY
@@ -1105,7 +784,7 @@
if msg[IN_SYNC_KEY]:
# Do not proceed if we have not got our vENET information yet.
- if len(self.uni_ports) > 0 or not self.xpon_support:
+ if len(self.uni_ports) == 0:
# Drop subscription....
insync, self._in_sync_subscription = self._in_sync_subscription, None
@@ -1117,14 +796,7 @@
# vENET information is created. Once xPON is removed, we need to create
# them from the information provided from the MIB upload UNI-G and other
# UNI related MEs.
- if not self.xpon_support:
- self.add_uni_ports()
- else:
- for uni in self.uni_ports:
- uni.add_logical_port(None, None)
- else:
- # SEBA - drop this one once xPON deprecated
- self._deferred = reactor.callLater(5, self.in_sync_handler, _topic, msg)
+ self.add_uni_ports()
except Exception as e:
self.log.exception('in-sync', e=e)
diff --git a/voltha/adapters/adtran_onu/flow/flow_entry.py b/voltha/adapters/adtran_onu/flow/flow_entry.py
index 7a90daf..0c37d1d 100644
--- a/voltha/adapters/adtran_onu/flow/flow_entry.py
+++ b/voltha/adapters/adtran_onu/flow/flow_entry.py
@@ -49,6 +49,7 @@
(FlowDirection.UNI, FlowDirection.ANI): FlowDirection.UPSTREAM,
(FlowDirection.ANI, FlowDirection.UNI): FlowDirection.DOWNSTREAM
}
+
# Well known EtherTypes
class EtherType(IntEnum):
EAPOL = 0x888E
@@ -63,7 +64,6 @@
UDP = 17
def __init__(self, flow, handler):
- self._flow = flow # TODO: Drop this reference once debugging done
self._handler = handler
self.flow_id = flow.id
self._flow_direction = FlowEntry.FlowDirection.OTHER
diff --git a/voltha/adapters/adtran_onu/omci/adtn_install_flow.py b/voltha/adapters/adtran_onu/omci/adtn_install_flow.py
index 5f155f1..4b920de 100644
--- a/voltha/adapters/adtran_onu/omci/adtn_install_flow.py
+++ b/voltha/adapters/adtran_onu/omci/adtn_install_flow.py
@@ -138,9 +138,6 @@
"""
self.log.info('perform-flow-install', vlan_vid=self._flow_entry.vlan_vid)
- if self._handler.xpon_support:
- self.deferred.callback('flow-install-nop') # xPON mode does not need this
-
def resources_available():
# TODO: Rework for non-xpon mode
return (len(self._handler.uni_ports) > 0 and
diff --git a/voltha/adapters/adtran_onu/omci/adtn_mib_sync.py b/voltha/adapters/adtran_onu/omci/adtn_mib_sync.py
index b2d3db4..e0d6b82 100644
--- a/voltha/adapters/adtran_onu/omci/adtn_mib_sync.py
+++ b/voltha/adapters/adtran_onu/omci/adtn_mib_sync.py
@@ -21,7 +21,7 @@
OpenOMCI MIB Synchronizer state machine for Adtran ONUs
"""
ADTN_RESYNC_DELAY = 120 # Periodically force a resync (lower for debugging)
- ADTN_AUDIT_DELAY = 0 # Disabled until after BBWF
+ ADTN_AUDIT_DELAY = 60
def __init__(self, agent, device_id, mib_sync_tasks, db, advertise_events=False):
"""
diff --git a/voltha/adapters/adtran_onu/omci/adtn_remove_flow.py b/voltha/adapters/adtran_onu/omci/adtn_remove_flow.py
index 214a5aa..53db01a 100644
--- a/voltha/adapters/adtran_onu/omci/adtn_remove_flow.py
+++ b/voltha/adapters/adtran_onu/omci/adtn_remove_flow.py
@@ -148,11 +148,7 @@
"""
self.log.info('perform-flow-removall')
- if self._handler.xpon_support:
- self.deferred.callback('flow-removal-nop') # xPON mode does not need this
-
def resources_available():
- # TODO: Rework for non-xpon mode
return (len(self._handler.uni_ports) > 0 and
len(self._pon.tconts) and
len(self._pon.gem_ports))
diff --git a/voltha/adapters/adtran_onu/omci/adtn_service_download_task.py b/voltha/adapters/adtran_onu/omci/adtn_service_download_task.py
index e4839e5..e2e6b54 100644
--- a/voltha/adapters/adtran_onu/omci/adtn_service_download_task.py
+++ b/voltha/adapters/adtran_onu/omci/adtn_service_download_task.py
@@ -78,13 +78,9 @@
self._input_tpid = AdtnServiceDownloadTask.default_tpid
self._output_tpid = AdtnServiceDownloadTask.default_tpid
- if self._handler.xpon_support:
- device = self._handler.adapter_agent.get_device(self.device_id)
- self._vid = device.vlan
- else:
- # TODO: TCIS below is just a test, may need 0x900...as in the xPON mode
- # self._vlan_tcis_1 = OMCI.DEFAULT_UNTAGGED_VLAN
- self._vid = OMCI.DEFAULT_UNTAGGED_VLAN
+ # TODO: TCIS below is just a test, may need 0x900...as in the xPON mode
+ # self._vlan_tcis_1 = OMCI.DEFAULT_UNTAGGED_VLAN
+ self._vid = OMCI.DEFAULT_UNTAGGED_VLAN
# Entity IDs. IDs with values can probably be most anything for most ONUs,
# IDs set to None are discovered/set
@@ -164,16 +160,9 @@
device = self._handler.adapter_agent.get_device(self.device_id)
def resources_available():
- # TODO: Rework for non-xpon mode
- if self._handler.xpon_support:
- return (device.vlan > 0 and
- len(self._handler.uni_ports) > 0 and
- len(self._pon.tconts) and
- len(self._pon.gem_ports))
- else:
- return (len(self._handler.uni_ports) > 0 and
- len(self._pon.tconts) and
- len(self._pon.gem_ports))
+ return (len(self._handler.uni_ports) > 0 and
+ len(self._pon.tconts) and
+ len(self._pon.gem_ports))
if self._handler.enabled and resources_available():
device.reason = 'Performing Service OMCI Download'
diff --git a/voltha/adapters/adtran_onu/omci/omci.py b/voltha/adapters/adtran_onu/omci/omci.py
index 5966d2c..a72a991 100644
--- a/voltha/adapters/adtran_onu/omci/omci.py
+++ b/voltha/adapters/adtran_onu/omci/omci.py
@@ -191,6 +191,8 @@
pon_ports = len(ani_g) if ani_g is not None else 0
uni_ports = len(uni_g) if uni_g is not None else 0
+ # For the UNI ports below, they are created after the MIB Sync event occurs
+ # and the onu handler adds the ONU
assert pon_ports == 1, 'Expected one PON/ANI port, got {}'.format(pon_ports)
assert uni_ports == len(self._handler.uni_ports), \
'Expected {} UNI port(s), got {}'.format(len(self._handler.uni_ports), uni_ports)
@@ -201,16 +203,6 @@
# Save entity_id of PON ports
self._handler.pon_ports[0].entity_id = ani_g.keys()[0]
- # Save entity_id for UNI ports (this is only for xPON mode code). For the
- # non-xPON mode, we save the entity IDs during the mib-in-sync handler when
- # we create the UNI ports.
-
- if self._handler.xpon_support:
- uni_entity_ids = uni_g.keys()
- uni_entity_ids.sort()
- for uni in self._handler.uni_ports:
- uni.entity_id = uni_entity_ids.pop(0)
-
self._total_tcont_count = ani_g.get('total-tcont-count')
self._qos_flexibility = config.qos_configuration_flexibility or 0
self._omcc_version = config.omcc_version or OMCCVersion.Unknown
@@ -279,6 +271,10 @@
OmciCCRxEvents.Connectivity)
self._connectivity_subscription = bus.subscribe(topic, self.onu_is_reachable)
+ # TODO: Watch for any MIB RESET events or detection of an ONU reboot.
+ # If it occurs, set _service_downloaded and _mib_download to false
+ # and make sure that we get 'new' capabilities
+
def _unsubscribe_to_events(self):
insync, self._in_sync_subscription = self._in_sync_subscription, None
connect, self._connectivity_subscription = self._connectivity_subscription, None
diff --git a/voltha/adapters/adtran_onu/onu_gem_port.py b/voltha/adapters/adtran_onu/onu_gem_port.py
index a8fe3ed..d2c3dad 100644
--- a/voltha/adapters/adtran_onu/onu_gem_port.py
+++ b/voltha/adapters/adtran_onu/onu_gem_port.py
@@ -30,8 +30,6 @@
multicast=False,
tcont_ref=None,
traffic_class=None,
- intf_ref=None,
- untagged=False,
name=None,
handler=None):
super(OnuGemPort, self).__init__(gem_id, alloc_id,
@@ -40,8 +38,6 @@
multicast=multicast,
tcont_ref=tcont_ref,
traffic_class=traffic_class,
- intf_ref=intf_ref,
- untagged=untagged,
name=name,
handler=handler)
self._entity_id = entity_id
@@ -77,8 +73,7 @@
tcont_ref=gem_port['tcont-ref'],
name=gem_port['name'],
traffic_class=gem_port['traffic-class'],
- handler=handler,
- untagged='untagged' in gem_port['name'].lower())
+ handler=handler)
@inlineCallbacks
def add_to_hardware(self, omci,
diff --git a/voltha/adapters/adtran_onu/onu_tcont.py b/voltha/adapters/adtran_onu/onu_tcont.py
index c94a339..f89829f 100644
--- a/voltha/adapters/adtran_onu/onu_tcont.py
+++ b/voltha/adapters/adtran_onu/onu_tcont.py
@@ -20,15 +20,15 @@
from voltha.extensions.omci.omci_me import TcontFrame
from voltha.extensions.omci.omci_defs import ReasonCodes
+
class OnuTCont(TCont):
"""
Adtran ONU specific implementation
"""
free_tcont_alloc_id = 0xFFFF
- def __init__(self, handler, alloc_id, traffic_descriptor, name=None, vont_ani=None):
- super(OnuTCont, self).__init__(alloc_id, traffic_descriptor,
- name=name, vont_ani=vont_ani)
+ 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
self.log = structlog.get_logger(device_id=handler.device_id, alloc_id=alloc_id)
@@ -41,12 +41,7 @@
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'],
- vont_ani=tcont['vont-ani'])
+ return OnuTCont(handler, tcont['alloc-id'], td, name=tcont['name'])
@inlineCallbacks
def add_to_hardware(self, omci, tcont_entity_id):
diff --git a/voltha/adapters/adtran_onu/uni_port.py b/voltha/adapters/adtran_onu/uni_port.py
index dc7baa5..5451e8a 100644
--- a/voltha/adapters/adtran_onu/uni_port.py
+++ b/voltha/adapters/adtran_onu/uni_port.py
@@ -20,15 +20,12 @@
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
-from omci.omci import OMCI
class UniPort(object):
"""Wraps southbound-port(s) support for ONU"""
- def __init__(self, handler, name, port_no, ofp_port_no, subscriber_vlan=None,
- untagged_vlan=None):
- self.log = structlog.get_logger(device_id=handler.device_id,
- port_no=port_no)
+ def __init__(self, handler, name, port_no, ofp_port_no):
+ self.log = structlog.get_logger(device_id=handler.device_id, port_no=port_no)
self._enabled = False
self._handler = handler
self._name = name
@@ -36,14 +33,11 @@
self._port_number = port_no
self._ofp_port_no = ofp_port_no # Set at by creator (vENET create)
self._logical_port_number = None # Set at time of logical port creation
- self._subscriber_vlan = subscriber_vlan
- self._untagged_vlan = untagged_vlan
self._entity_id = None # TODO: Use port number from UNI-G entity ID
self._mac_bridge_port_num = 0
self._admin_state = AdminState.ENABLED
self._oper_status = OperStatus.ACTIVE
-
# TODO Add state, stats, alarm reference, ...
pass
@@ -51,8 +45,8 @@
return "UniPort: {}:{}".format(self.name, self.port_number)
@staticmethod
- def create(handler, name, port_no, ofp_port_no, subscriber_vlan, untagged_vlan):
- port = UniPort(handler, name, port_no, ofp_port_no, subscriber_vlan, untagged_vlan)
+ def create(handler, name, port_no, ofp_port_no):
+ port = UniPort(handler, name, port_no, ofp_port_no)
return port
def _start(self):
@@ -60,7 +54,6 @@
self._admin_state = AdminState.ENABLED
self._oper_status = OperStatus.ACTIVE
-
self._update_adapter_agent()
# TODO: start h/w sync
# TODO: Enable the actual physical port?
@@ -71,7 +64,6 @@
self._admin_state = AdminState.DISABLED
self._oper_status = OperStatus.UNKNOWN
-
self._update_adapter_agent()
# TODO: Disable/power-down the actual physical port?
pass
@@ -79,7 +71,6 @@
def delete(self):
self.enabled = False
self._handler = None
- # TODO: anything else
def _cancel_deferred(self):
pass
@@ -137,20 +128,6 @@
self._entity_id = value
@property
- def subscriber_vlan(self):
- """
- Subscriber vlan assigned to this UNI
- :return: (int) subscriber vlan
- """
- return self._subscriber_vlan
-
- @subscriber_vlan.setter
- def subscriber_vlan(self, value):
- if value:
- if self._subscriber_vlan is None or self._subscriber_vlan != value:
- self._subscriber_vlan = value
-
- @property
def logical_port_number(self):
"""
Logical device port number (used as OpenFlow port for UNI)
@@ -173,43 +150,9 @@
# adapter_agent add_port also does an update of existing port
self._handler.adapter_agent.add_port(self._handler.device_id,
self.get_port())
-
except Exception as e:
self.log.exception('update-port', e=e)
- @staticmethod
- def decode_venet(venet_info):
- try:
- # Allow spaces or dashes as separator, select last as the
- # port number. UNI-1, UNI 1, and UNI 3-2-1 are the same
- port_no = int(venet_info['name'].replace(' ', '-').split('-')[-1:][0])
- subscriber_vlan = port_no
- untagged_vlan = OMCI.DEFAULT_UNTAGGED_VLAN
- try:
- # Subscriber VLAN and Untagged vlan are comma separated
- parts = venet_info['description'].split(',')
- sub_part = next((part for part in parts if 'vlan' in part.lower()), None)
- untagged_part = next((part for part in parts if 'untagged' in part.lower()), None)
- try:
- if sub_part is not None:
- subscriber_vlan = int(sub_part.split(':')[-1:][0])
- except Exception as e:
- pass
- try:
- if untagged_part is not None:
- untagged_vlan = int(untagged_part.split(':')[-1:][0])
- except Exception as e:
- pass
- except Exception as e:
- pass
-
- return port_no, subscriber_vlan, untagged_vlan
-
- except ValueError:
- pass
- except KeyError:
- pass
-
def get_port(self):
"""
Get the VOLTHA PORT object for this port
@@ -226,7 +169,7 @@
def port_id_name(self):
return 'uni-{}'.format(self._logical_port_number)
- def add_logical_port(self, openflow_port_no, subscriber_vlan=None,
+ def add_logical_port(self, openflow_port_no,
capabilities=OFPPF_10GB_FD | OFPPF_FIBER,
speed=OFPPF_10GB_FD):
@@ -245,18 +188,11 @@
# Use vENET provisioned values if none supplied
port_no = openflow_port_no or self._ofp_port_no
- vlan = subscriber_vlan or self._subscriber_vlan
if self._logical_port_number is None and port_no is not None:
self._logical_port_number = port_no
- self._subscriber_vlan = vlan
-
device = self._handler.adapter_agent.get_device(self._handler.device_id)
- if vlan is not None and device.vlan != vlan:
- device.vlan = vlan
- self._handler.adapter_agent.update_device(device)
-
openflow_port = ofp_port(
port_no=port_no,
hw_addr=mac_str_to_tuple('08:00:%02x:%02x:%02x:%02x' %
@@ -279,4 +215,3 @@
ofp_port=openflow_port,
device_id=device.id,
device_port_no=self._port_number))
- # TODO: Should we use the UNI object 'name' as the id for OpenFlow?