VOL-1234: Initial support for running without xPON CLI/NBI
Change-Id: I0d632a5d495f76ab4dbcbf473297328541a04bab
diff --git a/voltha/adapters/adtran_olt/adtran_device_handler.py b/voltha/adapters/adtran_olt/adtran_device_handler.py
index f6796ed..d506fa2 100644
--- a/voltha/adapters/adtran_olt/adtran_device_handler.py
+++ b/voltha/adapters/adtran_olt/adtran_device_handler.py
@@ -40,8 +40,8 @@
DEFAULT_MULTICAST_VLAN = 4000
DEFAULT_UTILITY_VLAN = 4094
-DEFAULT_UNTAGGED_VLAN = DEFAULT_UTILITY_VLAN # if RG does not send priority tagged frames
-#DEFAULT_UNTAGGED_VLAN = 4092
+BROADCOM_UNTAGGED_VLAN = 4091 # SEBA - For BBWF demo (BroadCom Default VLAN)
+DEFAULT_UNTAGGED_VLAN = BROADCOM_UNTAGGED_VLAN # if RG does not send priority tagged frames
_DEFAULT_RESTCONF_USERNAME = ""
_DEFAULT_RESTCONF_PASSWORD = ""
@@ -51,7 +51,9 @@
_DEFAULT_NETCONF_PASSWORD = ""
_DEFAULT_NETCONF_PORT = 830
-_STARTUP_RETRY_TIMEOUT = 5 # 5 seconds delay after activate failed before we
+_STARTUP_RETRY_TIMEOUT = 5 # 5 seconds delay after activate failed before we
+_DEFAULT_XPON_SUPPORTED = True # LOOK for the keywords 'xpon_support', SEBA
+ # for areas to clean up once xPON is deprecated
class AdtranDeviceHandler(object):
@@ -136,6 +138,8 @@
self.netconf_password = _DEFAULT_NETCONF_PASSWORD
self._netconf_client = None
+ # TODO: Decrement xPON once Technology Profiles completed
+ self.xpon_support = _DEFAULT_XPON_SUPPORTED
self.max_nni_ports = 1 # TODO: This is a VOLTHA imposed limit in 'flow_decomposer.py
# and logical_device_agent.py
# OMCI ZMQ Channel
@@ -273,8 +277,9 @@
parser.add_argument('--utility_vlan', '-B', action='store',
default='{}'.format(DEFAULT_UTILITY_VLAN),
help='VLAN for Untagged Frames from ONUs')
- parser.add_argument('--deprecated_option', '-X', action='store',
- default='not-used', help='Deprecated command')
+ parser.add_argument('--xpon_enable', '-X', action='store_true',
+ default=not _DEFAULT_XPON_SUPPORTED,
+ help='enable xPON (BBF WT-385) provisioning support')
try:
args = parser.parse_args(shlex.split(device.extra_args))
@@ -291,6 +296,10 @@
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
if not self.rest_username:
self.rest_username = 'NDE0NDRkNDk0ZQ==\n'.\
@@ -980,7 +989,7 @@
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)
+ gpon_info = self.get_xpon_info(port.pon_id) # SEBA
if gpon_info is not None and \
gpon_info['channel-terminations'] is not None and \
len(gpon_info['channel-terminations']) > 0:
diff --git a/voltha/adapters/adtran_olt/adtran_olt.py b/voltha/adapters/adtran_olt/adtran_olt.py
index 84a39c5..19e8511 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='0.20',
+ version='0.21',
config=AdapterConfig(log_level=LogLevel.INFO)
)
log.debug('adtran_olt.__init__', adapter_agent=adapter_agent)
diff --git a/voltha/adapters/adtran_olt/adtran_olt_handler.py b/voltha/adapters/adtran_olt/adtran_olt_handler.py
index 1403e1a..bf4c654 100644
--- a/voltha/adapters/adtran_olt/adtran_olt_handler.py
+++ b/voltha/adapters/adtran_olt/adtran_olt_handler.py
@@ -18,8 +18,10 @@
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 download import Download
from xpon.adtran_olt_xpon import AdtranOltXPON
from codec.olt_state import OltState
@@ -318,11 +320,11 @@
self.startup = self.rest_client.request('GET', self.GPON_PON_CONFIG_LIST_URI,
'pon-config')
try:
- results = yield self.startup
-
from codec.ietf_interfaces import IetfInterfacesState
from nni_port import MockNniPort
+ results = yield self.startup
+
ietf_interfaces = IetfInterfacesState(self.netconf_client)
if self.is_virtual_olt:
@@ -339,7 +341,8 @@
pon_id = data['pon-id']
port = ports[pon_id + 1]
port['pon-id'] = pon_id
- port['admin_state'] = AdminState.ENABLED if data.get('enabled', False)\
+ port['admin_state'] = AdminState.ENABLED \
+ if data.get('enabled', not self.xpon_support)\
else AdminState.DISABLED
except Exception as e:
@@ -983,18 +986,24 @@
return pon_id + 1 + 4 # Skip over uninitialized ports
def _port_number_to_pon_id(self, port):
- # return port - 1 - self.num_northbound_ports
+ if not self.xpon_support and self.is_uni_port(port):
+ # Convert to OLT device port
+ port = platform.intf_id_from_uni_port_num(port)
+
return port - 1 - 4 # Skip over uninitialized ports
def is_pon_port(self, port):
return self._port_number_to_pon_id(port) in self.southbound_ports
def is_uni_port(self, port):
- return port >= self._onu_offset(0) # TODO: Really need to rework this one...
+ 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
+ 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
@@ -1003,7 +1012,6 @@
# 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
@@ -1013,14 +1021,26 @@
return None, None, None
if flow_entry.flow_direction == FlowEntry.FlowDirection.UPSTREAM:
- # Upstream ACLs will have VID=Logical_port until VOL-460 is addressed
- # but User data flows have the correct VID / C-Tag.
+ 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:
- onu = next((onu for onu in pon_port.onus if
- onu.logical_port == vid), None)
- else:
+ 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):
@@ -1032,6 +1052,7 @@
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
@@ -1052,7 +1073,15 @@
return self.get_southbound_port(port).name
if self.is_uni_port(port):
- return self.northbound_ports[port].name
+ if self.xpon_support:
+ return self.northbound_ports[port].name
+ else:
+ # try: TODO: Remove this crap
+ # import voltha.protos.device_pb2 as dev_pb2
+ # return dev_pb2._PORT_PORTTYPE.values_by_number[port].name
+ # except Exception as err:
+ # pass
+ return 'uni-{}'.format(port)
if self.is_logical_port(port):
raise NotImplemented('TODO: Logical ports not yet supported')
@@ -1252,6 +1281,80 @@
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:
+ return onu_device
+
+ try:
+ from voltha.protos.voltha_pb2 import Device
+
+ # NOTE - channel_id of onu is set to intf_id
+ proxy_address = Device.ProxyAddress(device_id=self.device_id,
+ channel_id=intf_id, onu_id=onu_id,
+ onu_session_id=onu_id)
+
+ self.log.debug("added-onu", port_no=intf_id,
+ onu_id=onu_id, serial_number=serial_number,
+ proxy_address=proxy_address)
+
+ self.adapter_agent.add_onu_device(
+ parent_device_id=self.device_id,
+ parent_port_no=intf_id,
+ vendor_id=serial_number[:4],
+ proxy_address=proxy_address,
+ 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
+
+ except Exception as e:
+ self.log.exception('onu-activation-failed', e=e)
+ return None
+
+ def _seba_xpon_create(self, onu_device, intf_id, onu_id, tconts, gem_ports):
+ # For SEBA, send over tcont and gem_port information until tech profiles are ready
+ # tcont creation (onu)
+ from voltha.registry import registry
+ self.log.info('inject-tcont-gem-data-onu-handler', intf_id=intf_id,
+ onu_id=onu_id, tconts=tconts, gem_ports=gem_ports)
+
+ onu_adapter_agent = registry('adapter_loader').get_agent(onu_device.adapter)
+ if onu_adapter_agent is None:
+ self.log.error('onu_adapter_agent-could-not-be-retrieved',
+ onu_device=onu_device)
+
+ # In the OpenOLT version, this is where they signal the BroadCom ONU
+ # to enable OMCI. It is a call to the create_interface IAdapter method
+ #
+ # For the Adtran ONU, we are already running OpenOMCI. On the Adtran ONU, a call
+ # to create_interface vectors into the xpon_create call which should not
+ # recognize the type and raise a value assertion
+ try:
+ onu_adapter_agent.create_interface(onu_device,
+ OnuIndication(intf_id, onu_id))
+ except:
+ 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',
@@ -1605,3 +1708,12 @@
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):
+ self.name = 'Dummy ONU Indication'
+ self.intf_id = intf_id
+ self.onu_id = onu_id
+ self.oper_state = 'up'
+ self.admin_state = 'up'
diff --git a/voltha/adapters/adtran_olt/adtranolt_platform.py b/voltha/adapters/adtran_olt/adtranolt_platform.py
new file mode 100644
index 0000000..317d448
--- /dev/null
+++ b/voltha/adapters/adtran_olt/adtranolt_platform.py
@@ -0,0 +1,260 @@
+#
+# 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.
+#
+
+from voltha.protos.device_pb2 import Port
+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.
+##
+#######################################################################
+"""
+Encoding of identifiers
+=======================
+
+GEM port ID
+
+ GEM port id is unique per PON port and ranges
+
+ 9 3 2 0
+ +--------------+------+
+ | onu id | GEM |
+ | | idx |
+ +--------------+------+
+
+ GEM port id range (0..1023) is reserved, by standard
+ Minimum GEM Port on Adtran OLT is 2176 (0x880)
+ onu id = 7 bits = 128 ONUs per PON
+ GEM index = 3 bits = 8 GEM ports per ONU
+
+Alloc ID
+
+ Uniquely identifies a T-CONT
+ Ranges from 1024..16383 per ITU Standard
+ For Adtran, 1024..1919
+ Unique per PON interface
+
+ 9 7 6 0
+ +-----+------------+
+ | idx | onu id | + (Min Alloc ID)
+ +-----+------------+
+
+ onu id = 7 bits = 128 ONUs per PON
+ Alloc index = 3 bits = 64 GEM ports per ONU
+
+Flow id
+
+ Identifies a flow within a single OLT
+ Flow Id is unique per OLT
+ Multiple GEM ports can map to same flow id
+
+ 13 11 4 0
+ +--------+--------------+------+
+ | pon id | onu id | Flow |
+ | | | idx |
+ +--------+--------------+------+
+
+ 14 bits = 16384 flows (per OLT).
+
+ pon id = 4 bits = 16 PON ports
+ onu id = 7 bits = 128 ONUss per PON port
+ Flow index = 3 bits = 4 bi-directional flows per ONU
+ = 8 uni-directional flows per ONU
+
+
+Logical (OF) UNI port number
+
+ OpenFlow port number corresponding to PON UNI
+
+ 15 11 4 0
+ +--+--------+--------------+------+
+ |0 | pon id | onu id | 0 |
+ +--+--------+--------------+------+
+
+ pon id = 4 bits = 16 PON ports
+ onu id = 7 bits = 128 ONUs per PON port
+
+
+PON OLT (OF) port number
+
+ OpenFlow port number corresponding to PON OLT ports
+
+ 31 28 0
+ +--------+------------------------~~~------+
+ | 0x2 | pon intf id |
+ +--------+------------------------~~~------+
+
+"""
+
+MIN_TCONT_ALLOC_ID = 1024 # 1024..16383
+MAX_TCONT_ALLOC_ID = 16383
+
+# MIN_GEM_PORT_ID = 1023 # 1023..65534
+# MAX_GEM_PORT_ID = 65534
+
+MIN_GEM_PORT_ID = 2176 # 2176..4222
+MAX_GEM_PORT_ID = MIN_GEM_PORT_ID + 2046
+
+MAX_ONUS_PER_PON = 128
+MAX_TCONTS_PER_ONU = 7
+
+
+def mk_uni_port_num(intf_id, onu_id):
+ """
+ Create a unique virtual UNI port number based up on PON and ONU ID
+ :param intf_id:
+ :param onu_id: (int) ONU ID (0..max)
+ :return: (int) UNI Port number
+ """
+ return intf_id << 11 | onu_id << 4
+
+
+# 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
+
+ :param port_num: (int) virtual UNI / vENET port number on OLT PON
+ :return: (int) PON Port number (note, this is not the PON ID)
+ """
+ return (port_num >> 11) & 0xF
+
+
+def mk_alloc_id(_, onu_id, idx=0):
+ """
+ Allocate a TCONT Alloc-ID. This is only called by the OLT
+
+ :param _: (int) PON ID (not used)
+ :param onu_id: (int) ONU ID (0..MAX_ONUS_PER_PON-1)
+ :param idx: (int) TCONT Index (0..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
+ return alloc_id
+
+
+def mk_gemport_id(_, onu_id, idx=0):
+ """
+ Allocate a GEM-PORT ID. This is only called by the OLT
+
+ A 4-bit mask was used since we need a gvid for untagged-EAPOL
+ traffic and then up to 8 more for user-user data priority
+ levels.
+
+ :param _: (int) PON ID (0..n) - not used
+ :param onu_id: (int) ONU ID (0..MAX_ONUS_PER_PON-1)
+ :param idx: (int) GEM_PORT Index (0..15)
+ """
+ return MIN_GEM_PORT_ID + (onu_id << 4) + idx
+
+
+# def 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)
+ return intf_id
+ elif intf_type is Port.PON_OLT:
+ # OpenOLT sets bit 29 + intf_id. We start at 5 for now for PON 0
+ # return 0x2 << 28 | intf_id
+ return intf_id + 5 # see _pon_id_to_port_number
+ else:
+ raise Exception('Invalid port type')
+
+
+def intf_id_from_nni_port_num(port_num):
+ # OpenOLT starts at 128. We start at 1 (one-to-one mapping)
+ # return port_num - 128
+ return port_num
+
+
+def intf_id_to_intf_type(intf_id):
+ # if (2 << 28 ^ intf_id) < 16:
+ # return Port.PON_OLT
+ # elif 128 <= intf_id <= 132:
+ # return Port.ETHERNET_NNI
+ if 5 <= intf_id <= 20:
+ return Port.PON_OLT
+ elif 1 <= intf_id <= 4:
+ return Port.ETHERNET_NNI
+ else:
+ 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]:
+ # Not sure what fffd and the other is
+ return out_port in [1, 2, 3, 4, 0xfffd, 0xfffffffd]
+
+
+def is_downstream(in_port, out_port):
+ return not is_upstream(in_port, out_port)
diff --git a/voltha/adapters/adtran_olt/download.py b/voltha/adapters/adtran_olt/download.py
index 2639bdc..af28fdd 100644
--- a/voltha/adapters/adtran_olt/download.py
+++ b/voltha/adapters/adtran_olt/download.py
@@ -518,7 +518,7 @@
del_job_xml = """
<maintenance-jobs operation="delete" xmlns="http://www.adtran.com/ns/yang/adtran-maintenance-jobs"/>
"""
- dl = [client.edit_config(del_fs_xml),
- client.edit_config(del_job_xml)]
+ dl = [client.edit_config(del_fs_xml, ignore_delete_error=True),
+ client.edit_config(del_job_xml, ignore_delete_error=True)]
return defer.gatherResults(dl, consumeErrors=True)
diff --git a/voltha/adapters/adtran_olt/flow/acl.py b/voltha/adapters/adtran_olt/flow/acl.py
index 10063bd..42975d8 100644
--- a/voltha/adapters/adtran_olt/flow/acl.py
+++ b/voltha/adapters/adtran_olt/flow/acl.py
@@ -25,6 +25,7 @@
ACL_NAME_FORMAT = 'VOLTHA-ACL-{}-{}' # format(flow_entry.handler.device_id, flow_entry.flow.id)
ACL_NAME_REGEX_ALL = 'VOLTHA-ACL-*'
+
class ACL(object):
"""
Class to wrap Trap-to-Controller functionality
@@ -47,10 +48,9 @@
self._valid = self._decode()
def __str__(self):
- return 'ACL: {}, Installed: {}, L2: {}, L3/4: {}'.format(self.name,
- self._installed,
- self.is_l2_exception,
- self.is_l3_l4_exception)
+ return 'ACL: {}, Installed: {}, L2: {}, L3/4: {}'.\
+ format(self.name, self._installed, self.is_l2_exception,
+ self.is_l3_l4_exception)
@property
def name(self):
diff --git a/voltha/adapters/adtran_olt/flow/evc.py b/voltha/adapters/adtran_olt/flow/evc.py
index 0a762fc..17cf005 100644
--- a/voltha/adapters/adtran_olt/flow/evc.py
+++ b/voltha/adapters/adtran_olt/flow/evc.py
@@ -256,6 +256,7 @@
@inlineCallbacks
def _do_install(self):
# Install the EVC if needed
+ log.debug('do-install', valid=self._valid, installed=self._installed)
if self._valid and not self._installed:
# TODO: Currently install EVC and then MAPs. Can do it all in a single edit-config operation
@@ -281,8 +282,7 @@
# xml += EVC.SwitchingMethod.xml(self._switching_method)
xml += EVC._xml_trailer()
- log.debug("Creating EVC {}: '{}'".format(self.name, xml))
-
+ log.debug('create-evc', name=self.name, xml=xml)
try:
# Set installed to true while request is in progress
self._installed = True
@@ -291,7 +291,7 @@
self.status = '' if results.ok else results.error
except Exception as e:
- log.exception('Failed to install EVC', name=self.name, e=e)
+ log.exception('install-failed', name=self.name, e=e)
raise
# Install any associated EVC Maps
@@ -303,7 +303,7 @@
except Exception as e:
evc_map.status = 'Exception during EVC-MAP Install: {}'.format(e.message)
- log.exception(evc_map.status, e=e)
+ log.exception('evc-map-install-failed', e=e)
returnValue(self._installed and self._valid)
diff --git a/voltha/adapters/adtran_olt/flow/evc_map.py b/voltha/adapters/adtran_olt/flow/evc_map.py
index 9c5cbe3..07e35b2 100644
--- a/voltha/adapters/adtran_olt/flow/evc_map.py
+++ b/voltha/adapters/adtran_olt/flow/evc_map.py
@@ -146,10 +146,15 @@
def installed(self):
return self._installed
- @installed.setter
- def installed(self, value):
- assert not value, 'installed can only be reset' # Can only reset
- self._installed = False
+ @property
+ def needs_update(self):
+ """ True if an parameter/ACL/... needs update or map needs to be reflowed after a failure"""
+ return self._needs_update
+
+ @needs_update.setter
+ def needs_update(self, value):
+ assert not value, 'needs update can only be reset' # Can only reset
+ self._needs_update = False
@property
def name(self):
@@ -306,6 +311,8 @@
ports.extend(gems_and_vids[0])
return ports
+ log.debug('install-evc-map', valid=self._valid, gem_ports=gem_ports())
+
if self._valid and len(gem_ports()) > 0:
# Install ACLs first (if not yet installed)
work_acls = self._new_acls.copy()
@@ -318,12 +325,13 @@
# pass # TODO : do anything?
except Exception as e:
- log.exception('acl-install', name=self.name, e=e)
+ log.exception('acl-install-failed', name=self.name, e=e)
self._new_acls.update(work_acls)
raise
# Now EVC-MAP
if not self._installed or self._needs_update:
+ log.debug('needs-install-or-update', installed=self._installed, update=self._needs_update)
try:
self._cancel_deferred()
map_xml = self._ingress_install_xml(self._gem_ids_and_vid, work_acls.values(),
@@ -343,7 +351,7 @@
self._new_acls.update(work_acls)
except Exception as e:
- log.exception('map-install', name=self.name, e=e)
+ log.exception('evc-map-install-failed', name=self.name, e=e)
self._new_acls.update(work_acls)
raise
@@ -432,9 +440,12 @@
returnValue('Done')
def reflow_needed(self):
- log.debug('reflow-needed')
- reflow = not self.installed
- # TODO: implement
+ log.debug('reflow-needed', installed=self.installed, needs_update=self.needs_update)
+ reflow = not self.installed or self.needs_update
+
+ if not reflow:
+ pass # TODO: implement retrieve & compare of EVC Map parameters
+
return reflow
@staticmethod
@@ -494,6 +505,8 @@
assert flow.flow_direction == FlowEntry.FlowDirection.UPSTREAM, \
'Only Upstream flows additions are supported at this time'
+ log('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 \
else EVCMap.create_egress_map(flow, evc, dry_run=True)
@@ -529,6 +542,7 @@
try:
del self._flows[flow.flow_id]
+ log('remove-flow-to-evc', flow=flow, evc=evc)
# Remove any ACLs
acl_name = ACL.flow_to_name(flow)
acl = None
@@ -701,8 +715,8 @@
# flow, then this is a traditional EVC flow
evc.men_to_uni_tag_manipulation = EVC.Men2UniManipulation.POP_OUT_TAG_ONLY
- evc.switching_method = EVC.SwitchingMethod.DOUBLE_TAGGED # \
- # if self._c_tag is not None else EVC.SwitchingMethod.SINGLE_TAGGED
+ evc.switching_method = EVC.SwitchingMethod.DOUBLE_TAGGED \
+ if self._c_tag is not None else EVC.SwitchingMethod.SINGLE_TAGGED
try:
acl = ACL.create(flow)
diff --git a/voltha/adapters/adtran_olt/flow/flow_entry.py b/voltha/adapters/adtran_olt/flow/flow_entry.py
index dac6e1e..6d5ea37 100644
--- a/voltha/adapters/adtran_olt/flow/flow_entry.py
+++ b/voltha/adapters/adtran_olt/flow/flow_entry.py
@@ -15,13 +15,11 @@
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 *
from voltha.protos.openflow_13_pb2 import OFPP_MAX
-from twisted.internet import defer
from twisted.internet.defer import returnValue, inlineCallbacks, gatherResults
log = structlog.get_logger()
@@ -123,6 +121,9 @@
self.name, self.in_port, self.output, self.vlan_id, self.inner_vid,
self.eth_type, self.ip_protocol)
+ def __repr__(self):
+ return str(self)
+
@property
def name(self):
return self._name # TODO: Is a name really needed in production?
@@ -174,7 +175,6 @@
:param flow: (Flow) Flow entry passed to VOLTHA adapter
:param handler: (AdtranDeviceHandler) handler for the device
-
:return: (FlowEntry, EVC)
"""
# Exit early if it already exists
@@ -204,13 +204,12 @@
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-upstream-exists', flow=flow_entry)
+ 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
# 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
@@ -231,6 +230,7 @@
downstream_sig = flow_entry.downstream_signature
if downstream_sig is None:
+ log.debug('flow-entry-empty-downstream', flow=flow_entry)
return None, None
if downstream_sig not in downstream_sig_table:
@@ -256,8 +256,8 @@
if len(upstream_flows) == 0 and not downstream_flow.is_multicast_flow:
upstream_flows = None
- log.debug('flow-entry-search-results', flow=flow_entry, downstream_flow=downstream_flow,
- upstream_flows=upstream_flows)
+ 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)
@@ -281,6 +281,9 @@
:return: EVC object
"""
+ log.debug('flow-evc-and-maps', downstream_flow=downstream_flow,
+ upstream_flows=upstream_flows)
+
if (evc is None and downstream_flow is None) or upstream_flows is None:
return None
@@ -300,6 +303,9 @@
downstream_flow.evc = EVC(downstream_flow)
if not downstream_flow.evc.valid:
+ log.debug('flow-evc-and-maps-downstream-invalid',
+ downstream_flow=downstream_flow,
+ upstream_flows=upstream_flows)
return None
# Create EVC-MAPs. Note upstream_flows is empty list for multicast
@@ -332,6 +338,10 @@
all_maps_valid = all(flow.evc_map.valid for flow in upstream_flows) \
or downstream_flow.is_multicast_flow
+ log.debug('flow-evc-and-maps-downstream',
+ downstream_flow=downstream_flow,
+ upstream_flows=upstream_flows, all_valid=all_maps_valid)
+
return downstream_flow.evc if all_maps_valid else None
def get_utility_evc(self, upstream_flows=None, use_default_vlan_id=False):
@@ -355,6 +365,7 @@
"""
Examine flow rules and extract appropriate settings
"""
+ log.debug('start-decode')
status = self._decode_traffic_selector() and self._decode_traffic_treatment()
if status:
@@ -389,7 +400,7 @@
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, self.vlan_id, untagged_vlan = \
+ self._logical_port, uni_vid, untagged_vlan = \
self._handler.get_onu_port_and_vlans(self)
if self._needs_acl_support:
@@ -400,11 +411,17 @@
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
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)
+
# 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:
@@ -444,6 +461,8 @@
self.signature = upstream_sig
self.downstream_signature = downstream_sig
+ log.debug('flow-evc-decode', upstream_sig=self.signature, downstream_sig=self.downstream_signature)
+
return status
def _decode_traffic_selector(self):
@@ -465,19 +484,28 @@
elif field.type == VLAN_VID:
# log.info('*** field.type == VLAN_VID', value=field.vlan_vid & 0xfff)
- self.vlan_id = field.vlan_vid & 0xfff
+ if self.handler.xpon_support:
+ # Traditional xPON way
+ self.vlan_id = field.vlan_vid & 0xfff
+ 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
+
+ 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.info('*** field.type == VLAN_PCP', value=field.vlan_pcp)
+ log.debug('*** field.type == VLAN_PCP', value=field.vlan_pcp)
self.pcp = field.vlan_pcp
elif field.type == ETH_TYPE:
- # log.info('*** field.type == ETH_TYPE', value=field.eth_type)
+ log.debug('*** field.type == ETH_TYPE', value=field.eth_type)
self.eth_type = field.eth_type
elif field.type == IP_PROTO:
- # log.info('*** field.type == IP_PROTO', value=field.ip_proto)
+ log.debug('*** field.type == IP_PROTO', value=field.ip_proto)
self.ip_protocol = field.ip_proto
if self.ip_protocol not in _supported_ip_protocols:
@@ -485,20 +513,29 @@
return False
elif field.type == IPV4_DST:
- # log.info('*** field.type == IPV4_DST', value=field.ipv4_dst)
+ log.debug('*** field.type == IPV4_DST', value=field.ipv4_dst)
self.ipv4_dst = field.ipv4_dst
elif field.type == UDP_DST:
- # log.info('*** field.type == UDP_DST', value=field.udp_dst)
+ log.debug('*** field.type == UDP_DST', value=field.udp_dst)
self.udp_dst = field.udp_dst
elif field.type == UDP_SRC:
- # log.info('*** field.type == UDP_SRC', value=field.udp_src)
+ log.debug('*** field.type == UDP_SRC', value=field.udp_src)
self.udp_src = field.udp_src
elif field.type == METADATA:
- # log.info('*** field.type == METADATA', value=field.table_metadata)
- self.inner_vid = field.table_metadata
+ log.debug('*** field.type == METADATA', value=field.table_metadata)
+ if self.handler.xpon_support:
+ # Traditional xPON way
+ self.inner_vid = field.table_metadata
+ 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
+
+ log.debug('*** field.type == METADATA', value=field.table_metadata, inner_vid=self.inner_vid)
else:
log.warn('unsupported-selection-field', type=field.type)
@@ -520,17 +557,17 @@
pass # Handled earlier
elif act.type == POP_VLAN:
- # log.info('*** action.type == POP_VLAN')
+ log.debug('*** action.type == POP_VLAN')
self.pop_vlan += 1
elif act.type == PUSH_VLAN:
- # log.info('*** action.type == PUSH_VLAN', value=act.push)
+ 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)
elif act.type == SET_FIELD:
- # log.info('*** action.type == SET_FIELD', value=act.set_field.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:
@@ -676,7 +713,7 @@
for evc_map in evc_maps:
if reflow or evc_map.reflow_needed():
- evc_map.installed = False
+ evc_map.needs_update = False
if not evc_map.installed:
evc = evc_map.evc
diff --git a/voltha/adapters/adtran_olt/net/adtran_netconf.py b/voltha/adapters/adtran_olt/net/adtran_netconf.py
index 2671b19..4e39a6a 100644
--- a/voltha/adapters/adtran_olt/net/adtran_netconf.py
+++ b/voltha/adapters/adtran_olt/net/adtran_netconf.py
@@ -117,8 +117,9 @@
#
# self._session.raise_mode = RaiseMode:NONE
#
- # and the when you get a response back, you can check 'response.ok' to see if it is 'True'
- # if it is not, you can enumerate the 'response.errors' list for more information
+ # and the when you get a response back, you can check 'response.ok' to
+ # see if it is 'True' if it is not, you can enumerate the 'response.errors'
+ # list for more information
return self._session
@@ -240,7 +241,7 @@
def unlock(self, source):
"""
Get the requested data from the server
- :param rpc_string: RPC request
+ :param source: RPC request
:return: (deferred) for RpcReply
"""
@@ -267,18 +268,24 @@
@inlineCallbacks
def edit_config(self, config, target='running', default_operation='none',
- test_option=None, error_option=None):
+ test_option=None, error_option=None, ignore_delete_error=False):
"""
- Loads all or part of the specified config to the target configuration datastore with the ability to lock
- the datastore during the edit.
+ Loads all or part of the specified config to the target configuration datastore
+ with the ability to lock the datastore during the edit.
- :param config is the configuration, which must be rooted in the config element. It can be specified
- either as a string or an Element.format="xml"
+ :param config is the configuration, which must be rooted in the config element.
+ It can be specified either as a string or an Element.format="xml"
:param target is the name of the configuration datastore being edited
:param default_operation if specified must be one of { 'merge', 'replace', or 'none' }
:param test_option if specified must be one of { 'test_then_set', 'set' }
- :param error_option if specified must be one of { 'stop-on-error', 'continue-on-error', 'rollback-on-error' }
- The 'rollback-on-error' error_option depends on the :rollback-on-error capability.
+ :param error_option if specified must be one of { 'stop-on-error',
+ 'continue-on-error', 'rollback-on-error' } The
+ 'rollback-on-error' error_option depends on the
+ :rollback-on-error capability.
+ :param ignore_delete_error: (bool) For some startup deletes/clean-ups, we do a
+ delete high up in the config to get whole lists. If
+ these lists are empty, this helps suppress any error
+ message from NETConf on failure to delete an empty list
:return: (deferred) for RpcReply
"""
@@ -298,16 +305,22 @@
' xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">' + \
config + '</config>'
+ log.debug('netconf-request', config=config, target=target,
+ default_operation=default_operation)
+
rpc_reply = yield threads.deferToThread(self._do_edit_config, target,
config, default_operation,
test_option, error_option)
except Exception as e:
+ if ignore_delete_error and 'operation="delete"' in config.lower():
+ returnValue('ignoring-delete-error')
log.exception('edit_config', e=e, config=config, target=target)
raise
returnValue(rpc_reply)
- def _do_edit_config(self, target, config, default_operation, test_option, error_option):
+ def _do_edit_config(self, target, config, default_operation, test_option, error_option,
+ ignore_delete_error=False):
"""
Perform actual edit-config operation
"""
@@ -321,12 +334,13 @@
# error_option=error_option
)
- log.debug('response', response=response)
+ log.debug('netconf-response', response=response)
# To get XML, use response.xml
# To check status, use response.ok (boolean)
except RPCError as e:
- log.exception('do_edit_config', e=e, config=config, target=target)
+ if not ignore_delete_error or 'operation="delete"' not in config.lower():
+ log.exception('do_edit_config', e=e, config=config, target=target)
raise
return response
diff --git a/voltha/adapters/adtran_olt/net/adtran_rest.py b/voltha/adapters/adtran_olt/net/adtran_rest.py
index 9f2b990..a478d95 100644
--- a/voltha/adapters/adtran_olt/net/adtran_rest.py
+++ b/voltha/adapters/adtran_olt/net/adtran_rest.py
@@ -99,6 +99,7 @@
:param is_retry: (boolean) True if this method called recursively in order to recover
from a connection loss. Can happen sometimes in debug sessions
and in the real world.
+ :param suppress_error: (boolean) If true, do not output ERROR message on REST request failure
:return: (dict) On success with the proper results
"""
log.debug('request', method=method, uri=uri, data=data, retry=is_retry)
diff --git a/voltha/adapters/adtran_olt/onu.py b/voltha/adapters/adtran_olt/onu.py
index b855218..362688a 100644
--- a/voltha/adapters/adtran_olt/onu.py
+++ b/voltha/adapters/adtran_olt/onu.py
@@ -24,7 +24,7 @@
_MAX_EXPEDITE_COUNT = 5
_EXPEDITE_SECS = 2
-_HW_SYNC_SECS = 30
+_HW_SYNC_SECS = 60
class Onu(object):
@@ -32,8 +32,7 @@
Wraps an ONU
"""
MIN_ONU_ID = 0
- MAX_ONU_ID = 253 # G.984. 0..253, 254=reserved, 255=broadcast
- BROADCAST_ONU_ID = 255
+ MAX_ONU_ID = 1020 # 1021..2013 reserved
DEFAULT_PASSWORD = ''
def __init__(self, onu_info):
@@ -52,11 +51,16 @@
self._xpon_name = onu_info['xpon-name']
self._gem_ports = {} # gem-id -> GemPort
self._tconts = {} # alloc-id -> TCont
- self._onu_vid = onu_info['onu-vid']
- self.untagged_vlan = self._onu_vid
- self._uni_ports = [onu_info['onu-vid']] # TODO: Get rid of this
- assert len(self._uni_ports) == 1, 'Only one UNI port supported at this time'
- self._channel_id = onu_info['channel-id']
+ 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
@@ -259,6 +263,7 @@
@property
def logical_port(self):
"""Return the logical PORT number of this ONU's UNI"""
+ # TODO: once we support multiple UNIs, this needs to be revisited
return self._uni_ports[0]
@property
@@ -273,19 +278,25 @@
device_id = self.olt.device_id
try:
- 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)
+ 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
@@ -309,7 +320,7 @@
@property
def channel_id(self):
- return self._channel_id
+ return self._channel_id # SEBA (xPON mode may be only one using this. May also be no one)
@property
def serial_number_64(self):
@@ -388,12 +399,14 @@
name = 'onu-create-{}-{}-{}: {}'.format(self._pon_id, self._onu_id,
self._serial_number_base64, self._enabled)
- if not self._created:
+ first_sync = self._sync_tick if self._created else 3
+
+ if not self._created or reflow:
try:
yield self.olt.rest_client.request('POST', uri, data=data, name=name)
self._created = True
- except Exception as e: # TODO: Add breakpoint here during unexpected reboot test
+ except Exception as e:
self.log.exception('onu-create', e=e)
# See if it failed due to already being configured
url = AdtranOltHandler.GPON_ONU_CONFIG_URI.format(self._pon_id, self._onu_id)
@@ -402,19 +415,19 @@
try:
results = yield self.olt.rest_client.request('GET', uri, name=name)
self.log.debug('onu-create-check', results=results)
- if len(results) != 1 or results[0].get('serial-number', '') != self._serial_number_base64:
- raise e
+ if len(results) == 1 and results[0].get('serial-number', '') != self._serial_number_base64:
+ self._created = True
except Exception as e:
- self.log.exception('onu-exists-check', e=e)
- raise
+ 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
- first_sync = self._sync_tick
-
+ # Now set up all TConts & GEM-ports
for _, tcont in tconts.items():
try:
- yield self.add_tcont(tcont, reflow=reflow)
+ _results = yield self.add_tcont(tcont, reflow=reflow)
except Exception as e:
self.log.exception('add-tcont', tcont=tcont, e=e)
@@ -422,7 +435,7 @@
for _, gem_port in gem_ports.items():
try:
- yield self.add_gem_port(gem_port, reflow=reflow)
+ _results = yield self.add_gem_port(gem_port, reflow=reflow)
except Exception as e:
self.log.exception('add-gem-port', gem_port=gem_port, reflow=reflow, e=e)
@@ -669,8 +682,8 @@
reflow, self._resync_flows = self._resync_flows, False
return FlowEntry.sync_flows_by_onu(self, reflow=reflow)
- def failure(reason):
- # self.log.error('hardware-sync-get-config-failed', reason=reason)
+ def failure(_reason):
+ # self.log.error('hardware-sync-get-config-failed', reason=_reason)
pass
def reschedule(_):
@@ -873,9 +886,9 @@
for evc_map in evc_maps:
evc_map.remove_gem_port(gem_port)
- results = 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,
+ self._pon_id,
+ self.onu_id)
except RestInvalidResponseCode as e:
if e.code != 404:
self.log.exception('onu-delete', e=e)
@@ -895,5 +908,5 @@
@staticmethod
def gem_id_to_gvid(gem_id):
- """Calculate GEM VID for a given GEM port id"""
+ """Calculate GEM VID (gvid) for a given GEM port id"""
return gem_id - 2048
diff --git a/voltha/adapters/adtran_olt/pon_port.py b/voltha/adapters/adtran_olt/pon_port.py
index e0f7f61..eb1239f 100644
--- a/voltha/adapters/adtran_olt/pon_port.py
+++ b/voltha/adapters/adtran_olt/pon_port.py
@@ -20,7 +20,7 @@
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
@@ -46,11 +46,11 @@
_MCAST_ONU_ID = 253
_MCAST_ALLOC_BASE = 0x500
- _SUPPORTED_ACTIVATION_METHODS = ['autodiscovery'] # , 'autoactivate']
+ # AutoActivate should be used if xPON configuration is not supported
+ _SUPPORTED_ACTIVATION_METHODS = ['autodiscovery', 'autoactivate']
_SUPPORTED_AUTHENTICATION_METHODS = ['serial-number']
def __init__(self, parent, **kwargs):
-
super(PonPort, self).__init__(parent, **kwargs)
assert 'pon-id' in kwargs, 'PON ID not found'
@@ -72,22 +72,29 @@
self._onus = {} # serial_number-base64 -> ONU (allowed list)
self._onu_by_id = {} # onu-id -> ONU
- self._next_onu_id = Onu.MIN_ONU_ID + 128
+ 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._discovery_deferred = None # Specifically for ONU discovery
self._active_los_alarms = set() # ONU-ID
- # xPON configuration
+ # xPON configuration # SEBA
+ self._activation_method = 'autodiscovery' if parent.xpon_support else 'autoactivate'
- self._xpon_name = None
- self._downstream_fec_enable = False
- self._upstream_fec_enable = False
+ 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._deployment_range = 25000
self._authentication_method = 'serial-number'
self._mcast_aes = False
self._line_rate = 'down_10_up_10'
- self._activation_method = 'autodiscovery'
# Statistics
self.tx_bip_errors = 0
@@ -265,6 +272,10 @@
value = value.lower()
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
@@ -415,7 +426,6 @@
self.log.exception('onu-cleanup', onu_id=onu_id, e=e)
dl.append(self._set_pon_config("enabled", False))
-
return defer.gatherResults(dl, consumeErrors=True)
@inlineCallbacks
@@ -424,7 +434,8 @@
Set the PON Port to a known good state on initial port startup. Actual
PON 'Start' is done elsewhere
"""
- initial_port_state = AdminState.DISABLED
+ initial_port_state = AdminState.DISABLED if self._parent.xpon_support \
+ else AdminState.ENABLED
self.log.info('reset', initial_state=initial_port_state)
try:
@@ -516,7 +527,10 @@
data = json.dumps({leaf: value})
uri = AdtranOltHandler.GPON_PON_CONFIG_URI.format(self._pon_id)
name = 'pon-set-config-{}-{}-{}'.format(self._pon_id, leaf, str(value))
- return self._parent.rest_client.request('PATCH', uri, data=data, name=name)
+ # If no optics on PON, then PON config fails with status 400, suppress this
+ suppress_error = len(self.onu_ids) == 0
+ return self._parent.rest_client.request('PATCH', uri, data=data, name=name,
+ suppress_error=suppress_error)
def _discover_onus(self):
self.log.debug('discovery', state=self._admin_state, in_sync=self._in_sync)
@@ -565,9 +579,11 @@
dl.append(self._set_pon_config("deployment-range",
self.deployment_range))
+ # A little side note: FEC enable/disable cannot be changed and
+ # will remain in the previous status until an optical module
+ # is plugged in.
if self.downstream_fec_enable != config.downstream_fec_enable:
self._in_sync = False
- self._expedite_sync = True
dl.append(self._set_pon_config("downstream-fec-enable",
self.downstream_fec_enable))
@@ -584,7 +600,7 @@
self.log.debug('sync-pon-onu-results', config=hw_onus)
# ONU's have their own sync task, extra (should be deleted) are
- # handled here. Missing are handled by normal discovery mechanisms.
+ # handled here.
hw_onu_ids = frozenset(hw_onus.keys())
my_onu_ids = frozenset(self._onu_by_id.keys())
@@ -592,6 +608,14 @@
extra_onus = hw_onu_ids - my_onu_ids
dl = [self.delete_onu(onu_id) for onu_id in extra_onus]
+ if self.activation_method == "autoactivate":
+ # Autoactivation of ONUs requires missing ONU detection. If
+ # not found, create them here but let TCont/GEM-Port restore be
+ # handle by ONU H/w sync logic.
+ for onu in [self._onu_by_id[onu_id] for onu_id in my_onu_ids - hw_onu_ids
+ if self._onu_by_id.get(onu_id) is not None]:
+ dl.append(onu.create(dict(), dict(), reflow=True))
+
return defer.gatherResults(dl, consumeErrors=True)
def failure(reason, what):
@@ -639,7 +663,6 @@
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)
@@ -688,7 +711,6 @@
owner_info['onu_id'] = \
child_device.proxy_address.onu_id
owner_info['alloc_id'] = tcont.alloc_id
- # self.bal.create_scheduler(id, 'upstream', owner_info, 8)
else:
self.log.info('Invalid-ONU-event', olt_id=olt_id,
pon_ni=ind_info['_pon_id'], onu_data=ind_info)
@@ -757,7 +779,7 @@
self.log.debug('discovered-ONUs', list=discovered_onus)
# Only request discovery if activation is auto-discovery or auto-activate
- continue_discovery = ['autodiscovery'] # , 'autoactivate']
+ continue_discovery = ['autodiscovery', 'autoactivate']
if self._activation_method not in continue_discovery:
return set(), set()
@@ -775,11 +797,11 @@
: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()
@@ -809,17 +831,47 @@
except StopIteration:
# Can happen if vont-ani or ont-ani has not yet been configured
self.log.debug('no-vont-or-ont')
- return None
+ return None, False
except Exception as e:
self.log.exception('autodiscovery', e=e)
raise
else:
self.log.debug('not-serial-number-authentication')
- return None
+ 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)
+ 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()
+ 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
+
+ except StopIteration:
+ # Can happen if vont-ani or ont-ani has not yet been configured
+ self.log.error('no-vont-or-ont-autoactivate')
+ return None, False
+
+ except Exception as e:
+ self.log.exception('autoactivate', e=e)
+ raise
else:
- self.log.debug('not-auto-discovery')
- return None
+ self.log.debug('unsupported-activation-method', method=self.activation_method)
+ return None, False
onu_info = {
'device-id': self.olt.device_id,
@@ -833,29 +885,56 @@
'password': Onu.DEFAULT_PASSWORD,
't-conts': tconts,
'gem-ports': gem_ports,
- 'onu-vid': self.olt.get_onu_vid(onu_id),
- 'channel-id': self.olt.get_channel_id(self._pon_id, onu_id),
+ '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)]
+
# Hold off ONU activation until at least one GEM Port is defined.
- self.log.debug('onu-info', gem_ports=gem_ports)
+ self.log.debug('onu-info-tech-profiles', gem_ports=gem_ports)
# return onu_info
- return onu_info if len(gem_ports) > 0 and venet is not None else None
+ return onu_info, activate_onu
except Exception as e:
- self.log.exception('get-onu-info', e=e)
- return None
+ self.log.exception('get-onu-info-tech-profiles', e=e)
+ return None, False
@inlineCallbacks
def add_onu(self, serial_number_64, status):
+ """
+ Add an ONU to the PON
+
+ TODO: This needs major refactoring after xPON is deprecated to be more maintainable
+ """
serial_number = Onu.serial_number_to_string(serial_number_64)
self.log.info('add-onu', serial_number=serial_number,
serial_number_64=serial_number_64, status=status)
- onu_info = self._get_onu_info(serial_number)
- if onu_info is None:
+ # 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:
+ 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()
@@ -872,8 +951,18 @@
elif (serial_number_64 in self._onus and onu_id not in self._onu_by_id) or \
(serial_number_64 not in self._onus and onu_id in self._onu_by_id):
# May be here due to unmanaged power-cycle on OLT or fiber bounced for a
- # previously activated ONU. Drop it and add back on next discovery cycle
- self.delete_onu(onu_id)
+ # 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
elif len(self._onus) >= self.MAX_ONUS_SUPPORTED:
self.log.warning('max-onus-provisioned', count=len(self._onus))
@@ -886,10 +975,16 @@
self._onu_by_id[onu.onu_id] = onu
if onu is not None:
- try:
- tconts = onu_info['t-conts']
- gem_ports = onu_info['gem-ports']
+ tconts = onu_info['t-conts']
+ gem_ports = onu_info['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
+ serial_number,
+ tconts, gem_ports)
+ try:
# Add Multicast to PON on a per-ONU basis until xPON multicast support is ready
# In xPON/BBF, mcast gems tie back to the channel-pair
# MCAST VLAN IDs stored as a negative value
@@ -901,28 +996,25 @@
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)
- yield onu.create(tconts, gem_ports)
+ # TODO: Need to clean up TCont and GEM-Port on ONU delete in non-xPON mode
+ _results = yield onu.create(tconts, gem_ports)
except Exception as e:
self.log.exception('add-onu', serial_number=serial_number_64, e=e)
- del self._onus[serial_number_64]
- del self._onu_by_id[onu.onu_id]
+ # allowable exception. H/w re-sync will recover any issues
+ @property
def get_next_onu_id(self):
- used_ids = [onu.onu_id for onu in self.onus]
+ assert not self.olt.xpon_support, 'Only non-XPON mode allocates ONU IDs. xPON assigns tem'
+ return self._onu_id_pool.get_next()
- while True:
- onu_id = self._next_onu_id
- self._next_onu_id += 1
-
- if self._next_onu_id > Onu.MAX_ONU_ID:
- self._next_onu_id = Onu.MIN_ONU_ID + 128
-
- if onu_id not in used_ids:
- return onu_id
+ def release_onu_id(self, onu_id):
+ if not self.olt.xpon_support:
+ self._onu_id_pool.release(onu_id)
@inlineCallbacks
def _remove_from_hardware(self, onu_id):
@@ -946,6 +1038,7 @@
# Remove from any local dictionary
if onu_id in self._onu_by_id:
del self._onu_by_id[onu_id]
+ self.release_onu_id(onu.onu_id)
for sn_64 in [onu.serial_number_64 for onu in self.onus if onu.onu_id == onu_id]:
del self._onus[sn_64]
@@ -978,96 +1071,3 @@
assert len(self.olt.multicast_vlans) == 1, 'Only support 1 MCAST VLAN until BBF Support'
self._mcast_gem_ports[vlan] = mcast_gem
-
- @inlineCallbacks
- def channel_partition(self, name, partition=0, xpon_system=0, operation=None):
- """
- Delete/enable/disable a specified channel partition on this PON.
-
- When creating a new Channel Partition, create it disabled, then define any associated
- Channel Pairs. Then enable the Channel Partition.
-
- :param name: (string) Name of the channel partition
- :param partition: (int: 0..15) An index of the operator-specified channel subset
- in a NG-PON2 system. For XGS-PON, this is typically 0
- :param xpon_system: (int: 0..1048575) Identifies a specific xPON system
- :param operation: (string) 'delete', 'enable', or 'disable'
- """
- if operation.lower() not in ['delete', 'enable', 'disable']:
- raise ValueError('Unsupported operation: {}'.format(operation))
-
- try:
- xml = 'interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"'
-
- if operation.lower() is 'delete':
- xml += '<interface operation="delete">'
- else:
- xml += '<interface>'
- xml += '<type xmlns:adtn-xp="http://www.adtran.com/ns/yang/adtran-xpon">' +\
- 'adtn-xp:xpon-channel-partition</type>'
- xml += '<adtn-xp:channel-partition xmlns:adtn-xp="http://www.adtran.com/ns/yang/adtran-xpon">'
- xml += ' <adtn-xp:partition-id>{}</adtn-xp:partition-id>'.format(partition)
- xml += ' <adtn-xp:xpon-system>{}</adtn-xp:xpon-system>'.format(xpon_system)
- xml += '</adtn-xp:channel-partition>'
- xml += '<enabled>{}</enabled>'.format('true' if operation.lower() == 'enable' else 'false')
-
- xml += '<name>{}</name>'.format(name)
- xml += '</interface></interfaces>'
-
- results = yield self.olt.netconf_client.edit_config(xml)
- returnValue(results)
-
- except Exception as e:
- self.log.exception('channel_partition')
- raise
-
- @inlineCallbacks
- def channel_pair(self, name, partition, operation=None, **kwargs):
- """
- Create/delete a channel pair on a specific channel_partition for a PON
-
- :param name: (string) Name of the channel pair
- :param partition: (string) Name of the channel partition
- :param operation: (string) 'delete', 'enable', or 'disable'
- :param kwargs: (dict) Additional leaf settings if desired
- """
- if operation.lower() not in ['delete', 'enable', 'disable']:
- raise ValueError('Unsupported operation: {}'.format(operation))
-
- try:
- xml = 'interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"'
-
- if operation.lower() is 'delete':
- xml += '<interface operation="delete">'
- else:
- xml += '<interface>'
- xml += '<type xmlns:adtn-xp="http://www.adtran.com/ns/yang/adtran-xpon">' +\
- 'adtn-xp:xpon-channel-pair</type>'
- xml += '<adtn-xp:channel-pair xmlns:adtn-xp="http://www.adtran.com/ns/yang/adtran-xpon">'
- xml += ' <adtn-xp:channel-partition>{}</adtn-xp:channel-partition>'.format(partition)
- xml += ' <adtn-xp:channel-termination>channel-termination {}</adtn-xp:channel-termination>'.\
- format(self.pon_id)
- xml += ' <adtn-xp:upstream-admin-label>{}</adtn-xp:upstream-admin-label>'.\
- format(kwargs.get('upstream-admin-label', 1))
- xml += ' <adtn-xp:downstream-admin-label>{}</adtn-xp:downstream-admin-label>'.\
- format(kwargs.get('downstream-admin-label', 1))
- xml += ' <adtn-xp:upstream-channel-id>{}</adtn-xp:upstream-channel-id>'.\
- format(kwargs.get('upstream-channel-id', 15))
- xml += ' <adtn-xp:downstream-channel-id>{}</adtn-xp:downstream-channel-id>'.\
- format(kwargs.get('downstream-channel-id', 15))
- xml += ' <adtn-xp:downstream-channel-fec-enable>{}</adtn-xp:downstream-channel-fec-enable>'. \
- format('true' if kwargs.get('downstream-channel-fec-enable', True) else 'false')
- xml += ' <adtn-xp:upstream-channel-fec-enable>{}</adtn-xp:upstream-channel-fec-enable>'. \
- format('true' if kwargs.get('upstream-channel-fec-enable', True) else 'false')
- xml += '</adtn-xp:channel-pair>'
- # TODO: Add support for upstream/downstream FEC-enable coming from here and not hard-coded
-
- xml += '<name>{}</name>'.format(name)
- xml += '</interface></interfaces>'
-
- results = yield self.olt.netconf_client.edit_config(xml)
- returnValue(results)
-
- except Exception as e:
- self.log.exception('channel_pair')
- raise
diff --git a/voltha/adapters/adtran_olt/port.py b/voltha/adapters/adtran_olt/port.py
index 0373724..fd94c6d 100644
--- a/voltha/adapters/adtran_olt/port.py
+++ b/voltha/adapters/adtran_olt/port.py
@@ -30,9 +30,6 @@
STOPPED = 2 # Disabled
DELETING = 3 # Cleanup
- _SUPPORTED_ACTIVATION_METHODS = ['autodiscovery'] # , 'autoactivate']
- _SUPPORTED_AUTHENTICATION_METHODS = ['serial-number']
-
def __init__(self, parent, **kwargs):
assert parent, 'parent is None'
assert 'port_no' in kwargs, 'Port number not found'
@@ -50,9 +47,15 @@
self.sync_tick = 20.0
self.sync_deferred = None # For sync of PON config to hardware
- # TODO: Deprecate 'enabled' and use admin_state instead
- self._enabled = False
- self._admin_state = AdminState.DISABLED
+ # 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._oper_status = OperStatus.DISCOVERED
self._state = AdtnPort.State.INITIAL
@@ -120,18 +123,6 @@
assert isinstance(value, bool), 'enabled is a boolean'
self.admin_state = AdminState.ENABLED if value else AdminState.DISABLED
- # @property
- # def enabled(self):
- # return self._enabled
- #
- # @enabled.setter
- # def enabled(self, value):
- # assert isinstance(value, bool), 'enabled is a boolean'
- # if self._enabled != value:
- # if value:
- # self.start()
- # self.stop()
-
@property
def oper_status(self):
return self._oper_status
diff --git a/voltha/adapters/adtran_olt/xpon/adtran_olt_xpon.py b/voltha/adapters/adtran_olt/xpon/adtran_olt_xpon.py
index d1ca2e5..1bfe009 100644
--- a/voltha/adapters/adtran_olt/xpon/adtran_olt_xpon.py
+++ b/voltha/adapters/adtran_olt/xpon/adtran_olt_xpon.py
@@ -24,10 +24,12 @@
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
@@ -57,9 +59,188 @@
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 configuraiton data for a specific pon-id / channel-termination
+ 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
"""
diff --git a/voltha/adapters/adtran_olt/xpon/adtran_xpon.py b/voltha/adapters/adtran_olt/xpon/adtran_xpon.py
index 8c92c9b..61756d2 100644
--- a/voltha/adapters/adtran_olt/xpon/adtran_xpon.py
+++ b/voltha/adapters/adtran_olt/xpon/adtran_xpon.py
@@ -27,6 +27,7 @@
log = structlog.get_logger()
+# SEBA
class AdtranXPON(object):
"""
Class to abstract common OLT and ONU xPON operations
@@ -219,23 +220,24 @@
return None
- def create_tcont(self, tcont_data, traffic_descriptor_data):
+ def create_tcont(self, tcont_data, td_data):
"""
Create TCONT information
:param tcont_data:
- :param traffic_descriptor_data:
+ :param td_data:
"""
- log.debug('create-tcont', tcont=tcont_data, td=traffic_descriptor_data)
+ log.debug('create-tcont', tcont=tcont_data, td=td_data)
# Handle TD first, then TCONT
- try:
- self.xpon_create(traffic_descriptor_data)
+ if td_data is not None:
+ try:
+ self.xpon_create(td_data)
- except Exception as e:
- log.exception('td-create', td=traffic_descriptor_data)
+ except Exception as e:
+ log.exception('td-create', td=td_data)
try:
- td = self.traffic_descriptors.get(traffic_descriptor_data.name)
+ td = self.traffic_descriptors.get(td_data.name) if td_data is not None else None
self.xpon_create(tcont_data, td=td)
except Exception as e:
@@ -288,11 +290,13 @@
def xpon_create(self, data, td=None):
log.debug('xpon-create', data=data)
-
name = data.name
items, create_method, update_method, _ = self._get_xpon_collection(data)
if items is None:
+ from voltha.adapters.adtran_olt.adtran_olt_handler import OnuIndication
+ if isinstance(data, OnuIndication): # Ignore this
+ return
raise ValueError('Unknown data type: {}'.format(type(data)))
item_type, new_item = self._data_to_dict(data, td=td)
@@ -318,6 +322,8 @@
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)
@@ -373,6 +379,9 @@
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)
diff --git a/voltha/adapters/adtran_olt/xpon/olt_gem_port.py b/voltha/adapters/adtran_olt/xpon/olt_gem_port.py
index 1f1c916..c50a858 100644
--- a/voltha/adapters/adtran_olt/xpon/olt_gem_port.py
+++ b/voltha/adapters/adtran_olt/xpon/olt_gem_port.py
@@ -36,7 +36,8 @@
untagged=False,
name=None,
handler=None,
- is_mock=False):
+ is_mock=False,
+ pb_data=None):
super(OltGemPort, self).__init__(gem_id, alloc_id,
encryption=encryption,
omci_transport=omci_transport,
@@ -49,6 +50,7 @@
handler=handler)
self._is_mock = is_mock
self._timestamp = None
+ self.data = pb_data # Needed for non-xPON mode
@staticmethod
def create(handler, gem_port):
@@ -67,7 +69,8 @@
intf_ref=gem_port.get(port_ref),
handler=handler,
multicast=mcast,
- untagged=untagged)
+ untagged=untagged,
+ pb_data=gem_port['data'])
@property
def timestamp(self):
diff --git a/voltha/adapters/adtran_olt/xpon/olt_tcont.py b/voltha/adapters/adtran_olt/xpon/olt_tcont.py
index 3156d50..a409026 100644
--- a/voltha/adapters/adtran_olt/xpon/olt_tcont.py
+++ b/voltha/adapters/adtran_olt/xpon/olt_tcont.py
@@ -25,10 +25,12 @@
Adtran OLT specific implementation
"""
def __init__(self, alloc_id, traffic_descriptor,
- name=None, vont_ani=None, is_mock=False):
+ 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)
self._is_mock = is_mock
+ self.data = pb_data # Needed for non-xPON mode
@staticmethod
def create(tcont, td):
@@ -39,7 +41,8 @@
return OltTCont(tcont['alloc-id'], td,
name=tcont['name'],
- vont_ani=tcont['vont-ani'])
+ vont_ani=tcont['vont-ani'],
+ pb_data=tcont['data'])
@inlineCallbacks
def add_to_hardware(self, session, pon_id, onu_id):
@@ -57,7 +60,7 @@
try:
results = yield session.request('POST', uri, data=data, name=name,
suppress_error=False)
- except:
+ except Exception as _e:
results = None
if self.traffic_descriptor is not None:
diff --git a/voltha/adapters/adtran_olt/xpon/olt_traffic_descriptor.py b/voltha/adapters/adtran_olt/xpon/olt_traffic_descriptor.py
index 1aa3848..635e5e1 100644
--- a/voltha/adapters/adtran_olt/xpon/olt_traffic_descriptor.py
+++ b/voltha/adapters/adtran_olt/xpon/olt_traffic_descriptor.py
@@ -28,12 +28,14 @@
additional=TrafficDescriptor.AdditionalBwEligibility.DEFAULT,
best_effort=None,
name=None,
- is_mock=False):
+ is_mock=False,
+ pb_data=None):
super(OltTrafficDescriptor, self).__init__(fixed, assured, maximum,
additional=additional,
best_effort=best_effort,
name=name)
self._is_mock = is_mock
+ self.data = pb_data
@staticmethod
def create(traffic_disc):
@@ -56,7 +58,8 @@
traffic_disc['maximum-bandwidth'],
name=traffic_disc['name'],
best_effort=best_effort,
- additional=additional)
+ additional=additional,
+ pb_data=traffic_disc['data'])
@inlineCallbacks
def add_to_hardware(self, session, pon_id, onu_id, alloc_id):