VOL-1235: Adtran ONU: Add initial support of running without xPON
Change-Id: I2866a6989976a13729f91bc18a380dd7f26c1b65
diff --git a/voltha/adapters/adtran_onu/adtran_onu.py b/voltha/adapters/adtran_onu/adtran_onu.py
index 06bc4af..450273f 100755
--- a/voltha/adapters/adtran_onu/adtran_onu.py
+++ b/voltha/adapters/adtran_onu/adtran_onu.py
@@ -40,7 +40,7 @@
device_handler_class=AdtranOnuHandler,
name='adtran_onu',
vendor='Adtran Inc.',
- version='0.14',
+ version='0.15',
device_type='adtran_onu',
vendor_id='ADTN',
accepts_add_remove_flow_updates=False), # TODO: Support flow-mods
@@ -151,7 +151,6 @@
API to create various interfaces (only some PON interfaces as of now)
in the devices
"""
-
self.log.debug('create-interface', data=data)
handler = self.devices_handlers.get(device.id)
diff --git a/voltha/adapters/adtran_onu/adtran_onu_handler.py b/voltha/adapters/adtran_onu/adtran_onu_handler.py
index eab5b56..693d9bd 100644
--- a/voltha/adapters/adtran_onu/adtran_onu_handler.py
+++ b/voltha/adapters/adtran_onu/adtran_onu_handler.py
@@ -23,20 +23,23 @@
from voltha.extensions.kpi.onu.onu_pm_metrics import OnuPmMetrics
from voltha.extensions.kpi.onu.onu_omci_pm import OnuOmciPmMetrics
-from uuid import uuid4
from twisted.internet import reactor
from twisted.internet.defer import DeferredQueue, inlineCallbacks
from twisted.internet.defer import returnValue
+from voltha.registry import registry
from voltha.protos import third_party
from voltha.protos.common_pb2 import OperStatus, ConnectStatus
from common.utils.indexpool import IndexPool
from voltha.extensions.omci.omci_me import *
+import voltha.adapters.adtran_olt.adtranolt_platform as platform
+
_ = third_party
-_MAXIMUM_PORT = 128 # PON and UNI ports
+_MAXIMUM_PORT = 17 # Only one PON and UNI port at this time
_ONU_REBOOT_MIN = 90 # IBONT 602 takes about 3 minutes
_ONU_REBOOT_RETRY = 10
+BRDCM_DEFAULT_VLAN = 4091
class AdtranOnuHandler(AdtranXPON):
@@ -58,17 +61,20 @@
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)
self._unis = dict() # Port # -> UniPort
- self._pon = None
+ self._pon = PonPort.create(self, self._pon_port_number)
self._heartbeat = HeartBeat.create(self, device_id)
self._deferred = None
self._event_deferred = None
- self._port_number_pool = IndexPool(_MAXIMUM_PORT, 1)
-
- self._olt_created = False # True if deprecated method of OLT creating DA is used
+ # 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)
@@ -119,10 +125,6 @@
# TODO: Anything else
@property
- def olt_created(self):
- return self._olt_created # ONU was created with deprecated 'child_device_detected' call
-
- @property
def openomci(self):
return self._openomci
@@ -185,9 +187,7 @@
def stop(self):
assert not self._enabled, 'Stop should only be called if disabled'
- #
- # TODO: Perform common shutdown tasks here
- #
+
self._cancel_deferred()
# Drop registration for adapter messages
@@ -225,10 +225,6 @@
assert device.parent_id, 'Invalid Parent ID'
assert device.proxy_address.device_id, 'Invalid Device ID'
- if device.vlan:
- # vlan non-zero if created via legacy method (not xPON).
- self._olt_created = True
-
# register for proxied messages right away
self.proxy_address = device.proxy_address
self.adapter_agent.register_for_proxied_messages(device.proxy_address)
@@ -243,23 +239,18 @@
device.connect_status = ConnectStatus.UNKNOWN
# Register physical ports. Should have at least one of each
- self._pon = PonPort.create(self, self._next_port_number)
self.adapter_agent.add_port(device.id, self._pon.get_port())
- if self._olt_created:
- # vlan non-zero if created via legacy method (not xPON). Also
- # Set a random serial number since not xPON based
+ def xpon_not_found():
+ if not self.xpon_support:
+ # Start things up for this ONU Handler.
+ self.enabled = True
- uni_port = UniPort.create(self, self._next_port_number, device.vlan,
- 'deprecated', device.vlan, None)
- self._unis[uni_port.port_number] = uni_port
- self.adapter_agent.add_port(device.id, uni_port.get_port())
-
- device.serial_number = uuid4().hex
- uni_port.add_logical_port(device.vlan, subscriber_vlan=device.vlan)
-
- # Start things up for this ONU Handler.
- 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)
# reference of uni_port is required when re-enabling the device if
# it was disabled previously
@@ -339,179 +330,157 @@
self.pm_metrics.update(pm_config)
@inlineCallbacks
- def update_flow_table(self, device, flows):
+ def update_flow_table(self, flows):
#
# We need to proxy through the OLT to get to the ONU
# Configuration from here should be using OMCI
#
- # self.log.info('bulk-flow-update', device_id=device.id, flows=flows)
+ self.log.debug('bulk-flow-update', flows=flows)
import voltha.core.flow_decomposer as fd
from voltha.protos.openflow_13_pb2 import OFPXMC_OPENFLOW_BASIC
def is_downstream(port):
- return port == 100 # Need a better way
+ return port == self._pon_port_number
def is_upstream(port):
return not is_downstream(port)
- omci = self.openomci.omci_cc
-
for flow in flows:
- _type = None
- _port = None
- _vlan_vid = None
- _udp_dst = None
- _udp_src = None
- _ipv4_dst = None
- _ipv4_src = None
- _metadata = None
- _output = None
- _push_tpid = None
- _field = None
- _set_vlan_vid = None
- self.log.info('bulk-flow-update', device_id=device.id, flow=flow)
+ match = {
+ 'in_port': None,
+ # 'etype': None, # These remaining key-value
+ # 'proto': None, # pairs are set if found in the
+ # 'vlan_vid': None, # flow information
+ # 'inner_vid': None,
+ # 'vlan_pcp': None,
+ # 'udp_dst': None,
+ # 'udp_src': None,
+ # 'ipv4_dst': None,
+ # 'ipv4_src': None,
+ }
+ actions = {
+ 'out_port': None,
+ # 'push_tpid': None, # These remaining key-value
+ # 'pop_vlan': None, (bool) # pairs are set if found in the
+ # 'set_vlan_vid': None, # flow information
+ }
+ self.log.debug('bulk-flow-update', flow=flow)
try:
- _in_port = fd.get_in_port(flow)
- assert _in_port is not None
-
- if is_downstream(_in_port):
- self.log.info('downstream-flow')
- elif is_upstream(_in_port):
- self.log.info('upstream-flow')
- else:
- raise Exception('port should be 1 or 2 by our convention')
-
_out_port = fd.get_out_port(flow) # may be None
- self.log.info('out-port', out_port=_out_port)
+ self.log.debug('out-port', out_port=_out_port)
for field in fd.get_ofb_fields(flow):
- if field.type == fd.ETH_TYPE:
- _type = field.eth_type
- self.log.info('field-type-eth-type',
- eth_type=_type)
+ if field.type == fd.IN_PORT:
+ assert match['in_port'] is None, \
+ 'Only a single input port is supported'
+ match['in_port'] = field.port
+ self.log.debug('field-type-in-port', in_port=field.port)
+
+ elif field.type == fd.ETH_TYPE:
+ match['etype'] = field.eth_type
+ self.log.debug('field-type-eth-type', eth_type=field.eth_type)
elif field.type == fd.IP_PROTO:
- _proto = field.ip_proto
- self.log.info('field-type-ip-proto',
- ip_proto=_proto)
-
- elif field.type == fd.IN_PORT:
- _port = field.port
- self.log.info('field-type-in-port',
- in_port=_port)
+ match['proto'] = field.ip_proto
+ self.log.debug('field-type-ip-proto', ip_proto=field.ip_proto)
elif field.type == fd.VLAN_VID:
- _vlan_vid = field.vlan_vid & 0xfff
- self.log.info('field-type-vlan-vid',
- vlan=_vlan_vid)
+ match['vlan_vid'] = field.vlan_vid & 0xfff
+ self.log.debug('field-type-vlan-vid', vlan=field.vlan_vid & 0xfff)
elif field.type == fd.VLAN_PCP:
- _vlan_pcp = field.vlan_pcp
- self.log.info('field-type-vlan-pcp',
- pcp=_vlan_pcp)
+ match['vlan_pcp'] = field.vlan_pcp
+ self.log.debug('field-type-vlan-pcp', pcp=field.vlan_pcp)
elif field.type == fd.UDP_DST:
- _udp_dst = field.udp_dst
- self.log.info('field-type-udp-dst',
- udp_dst=_udp_dst)
+ match['udp_dst'] = field.udp_dst
+ self.log.debug('field-type-udp-dst', udp_dst=field.udp_dst)
elif field.type == fd.UDP_SRC:
- _udp_src = field.udp_src
- self.log.info('field-type-udp-src',
- udp_src=_udp_src)
+ match['udp_src'] = field.udp_src
+ self.log.debug('field-type-udp-src', udp_src=field.udp_src)
elif field.type == fd.IPV4_DST:
- _ipv4_dst = field.ipv4_dst
- self.log.info('field-type-ipv4-dst',
- ipv4_dst=_ipv4_dst)
+ match['ipv4_dst'] = field.ipv4_dst
+ self.log.debug('field-type-ipv4-dst', ipv4_dst=field.ipv4_dst)
elif field.type == fd.IPV4_SRC:
- _ipv4_src = field.ipv4_src
- self.log.info('field-type-ipv4-src',
- ipv4_dst=_ipv4_src)
+ match['ipv4_src'] = field.ipv4_src
+ self.log.debug('field-type-ipv4-src', ipv4_dst=field.ipv4_src)
elif field.type == fd.METADATA:
- _metadata = field.table_metadata
- self.log.info('field-type-metadata',
- metadata=_metadata)
+ match['inner_vid'] = field.table_metadata
+ self.log.debug('field-type-metadata', metadata=field.table_metadata)
else:
- raise NotImplementedError('field.type={}'.format(
- field.type))
+ raise NotImplementedError('field.type={}'.format(field.type))
for action in fd.get_actions(flow):
-
if action.type == fd.OUTPUT:
- _output = action.output.port
- self.log.info('action-type-output',
- output=_output, in_port=_in_port)
+ actions['out_port'] = action.output.port
+ self.log.debug('action-type-output', output=action.output.port)
elif action.type == fd.POP_VLAN:
- self.log.info('action-type-pop-vlan',
- in_port=_in_port)
+ actions['pop_vlan'] = True
+ self.log.debug('action-type-pop-vlan')
elif action.type == fd.PUSH_VLAN:
- _push_tpid = action.push.ethertype
- self.log.info('action-type-push-vlan',
- push_tpid=_push_tpid, in_port=_in_port)
+ actions['push_tpid'] = action.push.ethertype
+ self.log.debug('action-type-push-vlan',
+ push_tpid=action.push.ethertype)
+
if action.push.ethertype != 0x8100:
- self.log.error('unhandled-tpid',
- ethertype=action.push.ethertype)
+ self.log.error('unsupported-tpid',
+ ethertype=action.push.ethertype,
+ in_port=match['in_port'])
elif action.type == fd.SET_FIELD:
_field = action.set_field.field.ofb_field
- assert (action.set_field.field.oxm_class ==
- OFPXMC_OPENFLOW_BASIC)
- self.log.info('action-type-set-field',
- field=_field, in_port=_in_port)
+ self.log.debug('action-type-set-field', field=_field)
+ assert (action.set_field.field.oxm_class == OFPXMC_OPENFLOW_BASIC)
+
if _field.type == fd.VLAN_VID:
- _set_vlan_vid = _field.vlan_vid & 0xfff
- self.log.info('set-field-type-valn-vid', _set_vlan_vid)
+ actions['set_vlan_vid'] = _field.vlan_vid & 0xfff
+ self.log.debug('set-field-type-vlan-vid', actions['set_vlan_vid'])
else:
self.log.error('unsupported-action-set-field-type',
- field_type=_field.type)
+ field_type=_field.type,
+ in_port=match['in_port'])
else:
- self.log.error('unsupported-action-type',
- action_type=action.type, in_port=_in_port)
- #
- # All flows created from ONU adapter should be OMCI based
- #
- if _vlan_vid == 0 and _set_vlan_vid != None and _set_vlan_vid != 0:
- # allow priority tagged packets
- # Set AR - ExtendedVlanTaggingOperationConfigData
- # 514 - RxVlanTaggingOperationTable - add VLAN <cvid> to priority tagged pkts - c-vid
+ self.log.error('unsupported-action-type', action_type=action.type,
+ in_port=match['in_port'])
- results = yield omci.send_delete_vlan_tagging_filter_data(0x2102)
+ assert match['in_port'] is not None, 'No input port specified'
+ assert actions['out_port'] is not None, 'No output port specified'
- # self.send_set_vlan_tagging_filter_data(0x2102, _set_vlan_vid)
- results = yield omci.send_create_vlan_tagging_filter_data(
- 0x2102,
- _set_vlan_vid)
-
- results = yield omci.send_set_extended_vlan_tagging_operation_vlan_configuration_data_untagged(
- 0x202,
- 0x1000,
- _set_vlan_vid)
-
- results = yield omci.send_set_extended_vlan_tagging_operation_vlan_configuration_data_single_tag(
- 0x202,
- 8,
- 0,
- 0,
- 1,
- 8,
- _set_vlan_vid)
-
- # Set AR - ExtendedVlanTaggingOperationConfigData
- # 514 - RxVlanTaggingOperationTable - add VLAN <cvid> to priority tagged pkts - c-vid
- '''
- results = yield omci.send_set_extended_vlan_tagging_operation_vlan_configuration_data_single_tag(0x205, 8, 0, 0,
-
- '''
+ _is_upstream = is_upstream(match['in_port'])
except Exception as e:
- self.log.exception('failed-to-install-flow', e=e, flow=flow)
+ self.log.exception('failed-to-decode-flow', e=e)
+ continue
+
+ # 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 match.get('vlan_vid') is None \
+ and match.get('etype') is not None:
+ continue
+
+ # Also ignore upstream untagged/priority tag that sets priority tag
+ # 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 (match.get('vlan_vid') == 0 and action.get('set_vlan_vid') == 0) or \
+ (match.get('vlan_vid') is None and action.get('set_vlan_vid') == 0
+ and not _is_upstream):
+ continue
+
+ from omci.adtn_install_flow import AdtnInstallFlowTask
+ task = AdtnInstallFlowTask(self.openomci, self, match, action, _is_upstream)
+ self.openomci.onu_omci_device.task_runner.queue_task(task)
@inlineCallbacks
def reboot(self):
@@ -634,34 +603,8 @@
self._pon.get_port())
self._pon.enabled = False
- # Send Uni Admin State Down
-
- # ethernet_uni_entity_id = 0x101
- # omci = self._handler.omci
- # attributes = dict(
- # administrative_state=1 # - lock
- # )
- # frame = PptpEthernetUniFrame(
- # ethernet_uni_entity_id, # Entity ID
- # attributes=attributes # See above
- # ).set()
- # results = yield omci.send(frame)
- #
- # status = results.fields['omci_message'].fields['success_code']
- # failed_attributes_mask = results.fields['omci_message'].fields['failed_attributes_mask']
- # unsupported_attributes_mask = results.fields['omci_message'].fields['unsupported_attributes_mask']
- # self.log.debug('set-pptp-ethernet-uni', status=status,
- # failed_attributes_mask=failed_attributes_mask,
- # unsupported_attributes_mask=unsupported_attributes_mask)
-
-
- # Just updating the port status may be an option as well
- # port.ofp_port.config = OFPPC_NO_RECV
- # yield self.adapter_agent.update_logical_port(logical_device_id,
- # port)
# Unregister for proxied message
- self.adapter_agent.unregister_for_proxied_messages(
- device.proxy_address)
+ self.adapter_agent.unregister_for_proxied_messages(device.proxy_address)
# TODO:
# 1) Remove all flows from the device
@@ -689,15 +632,11 @@
# Re-enable the ports on that device
self.adapter_agent.enable_all_ports(self.device_id)
- # Refresh the port reference
- # self.uni_port = self._get_uni_port() deprecated
-
# Add the pon port reference to the parent
if self._pon is not None:
self._pon.enabled = True
self.adapter_agent.add_port_reference_to_parent(device.id,
self._pon.get_port())
-
# Update the connect status to REACHABLE
device.connect_status = ConnectStatus.REACHABLE
self.adapter_agent.update_device(device)
@@ -707,15 +646,10 @@
self.logical_device_id = parent_device.parent_id
assert self.logical_device_id, 'Invalid logical device ID'
- if self.olt_created:
- # vlan non-zero if created via legacy method (not xPON)
- self.uni_port('deprecated').add_logical_port(device.vlan, device.vlan,
- subscriber_vlan=device.vlan)
- else:
- # 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)
+ # 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)
device = self.adapter_agent.get_device(device.id)
device.oper_status = OperStatus.ACTIVE
@@ -726,8 +660,7 @@
self.adapter_agent.update_device(device)
self.log.info('re-enabled', device_id=device.id)
- self._pon._dev_info_loaded = False
- self._bridge_initialized = False
+
except Exception, e:
self.log.exception('error-reenabling', e=e)
@@ -754,6 +687,8 @@
: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']
@@ -771,6 +706,9 @@
: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)
@@ -798,15 +736,22 @@
: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)
@@ -822,9 +767,14 @@
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
@@ -863,46 +813,109 @@
# Start of actual work (what actually does something)
# TODO: Clean this up. Use looked up UNI
- if self._olt_created:
- uni_port = self.uni_port('deprecated')
+ # vlan non-zero if created via legacy method (not xPON). Also
+ # Set a random serial number since not xPON based
- else:
- # 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)
- device = self.adapter_agent.get_device(self.device_id)
- 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
- uni_port = UniPort.create(self, venet['name'],
- self._next_port_number,
- ofp_port_no,
- subscriber_vlan,
- untagged_vlan)
+ # 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)
- self._unis[uni_port.port_number] = uni_port
- self.adapter_agent.add_port(device.id, uni_port.get_port())
+ device = self.adapter_agent.get_device(self.device_id)
+ self.adapter_agent.add_port(device.id, uni_port.get_port())
- # If the PON has already synchronized, add the logical port now
- # since we know we have been activated
+ self._unis[uni_port.port_number] = uni_port
- if self._pon is not None and self.openomci.connected:
- uni_port.add_logical_port(ofp_port_no, subscriber_vlan=subscriber_vlan)
+ # 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 = venet['enabled']
+ uni_port.enabled = enable
- return venet
+ def add_uni_ports(self):
+ """ Called after in-sync achieved and not in xPON mode"""
+ # 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 = BRDCM_DEFAULT_VLAN # TODO: Need a better way to define this
+
+ 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.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)
+
+ self.log.debug("created-uni-port", uni=uni_port)
+
+ self.adapter_agent.add_port(device.id, uni_port.get_port())
+ parent_device = self.adapter_agent.get_device(device.parent_id)
+
+ parent_adapter_agent = registry('adapter_loader').get_agent(parent_device.adapter)
+ if parent_adapter_agent is None:
+ self.log.error('olt-adapter-agent-could-not-be-retrieved')
+
+ parent_adapter_agent.add_port(device.parent_id, uni_port.get_port())
+
+ self._unis[uni_port.port_number] = uni_port
+
+ # 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,
+ pon_port)
+ # Find index where this ONU peer is (should almost always be zero)
+ d = [i for i, e in enumerate(pon_port.peers) if
+ e.port_no == intf_id and e.device_id == device.parent_id]
+
+ if len(d) > 0:
+ pon_port.peers[d[0]].port_no = uni_port.port_number
+ self.adapter_agent.add_port_reference_to_parent(self.device_id,
+ pon_port)
+ self.adapter_agent.update_device(device)
+
+ # TODO: only one uni/pptp for now. flow bug in openolt
def on_venet_modify(self, venet, update, diffs):
- # Look up the associated UNI port
+ if not self.xpon_support:
+ return
- if self._olt_created:
- uni_port = self.uni_port('deprecated')
- else:
- uni_port = self.uni_port(venet['name'])
+ # 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
@@ -920,12 +933,11 @@
return update
def on_venet_delete(self, venet):
- # Look up the associated UNI port
+ if not self.xpon_support:
+ return
- if self._olt_created:
- uni_port = self.uni_port('deprecated')
- else:
- uni_port = self.uni_port(venet['name'])
+ # 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
@@ -1158,7 +1170,7 @@
if msg[IN_SYNC_KEY]:
# Do not proceed if we have not got our vENET information yet.
- if len(self.uni_ports) > 0:
+ if len(self.uni_ports) > 0 or not self.xpon_support:
# Drop subscription....
insync, self._in_sync_subscription = self._in_sync_subscription, None
@@ -1170,10 +1182,13 @@
# 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.
-
- for uni in self.uni_ports:
- uni.add_logical_port(None, None)
+ 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)
except Exception as e:
diff --git a/voltha/adapters/adtran_onu/heartbeat.py b/voltha/adapters/adtran_onu/heartbeat.py
index f0efe30..4a7ab1f 100644
--- a/voltha/adapters/adtran_onu/heartbeat.py
+++ b/voltha/adapters/adtran_onu/heartbeat.py
@@ -69,14 +69,10 @@
@property
def check_item(self):
- # return 'vendor_id' if self._handler.olt_created else 'serial_number'
- return 'vendor_id' if self._handler.olt_created else 'vendor_id'
+ return 'vendor_id'
@property
def check_value(self):
- if self._handler.olt_created:
- return 'ADTN'
-
# device = self._handler.adapter_agent.get_device(self._device_id)
# return device.serial_number
return 'ADTN'
diff --git a/voltha/adapters/adtran_onu/omci/adtn_install_flow.py b/voltha/adapters/adtran_onu/omci/adtn_install_flow.py
new file mode 100644
index 0000000..f3e2cee
--- /dev/null
+++ b/voltha/adapters/adtran_onu/omci/adtn_install_flow.py
@@ -0,0 +1,309 @@
+#
+# 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 twisted.internet import reactor
+from twisted.internet.defer import inlineCallbacks, returnValue, TimeoutError, failure
+from voltha.extensions.omci.omci_me import *
+from voltha.extensions.omci.tasks.task import Task
+from voltha.extensions.omci.omci_defs import *
+
+OP = EntityOperations
+RC = ReasonCodes
+
+
+class ServiceDownloadFailure(Exception):
+ """
+ This error is raised by default when the download fails
+ """
+
+
+class ServiceResourcesFailure(Exception):
+ """
+ This error is raised by when one or more resources required is not available
+ """
+
+
+class AdtnInstallFlowTask(Task):
+ """
+ OpenOMCI MIB Download Example - Service specific
+
+ This task takes the legacy OMCI 'script' for provisioning the Adtran ONU
+ and converts it to run as a Task on the OpenOMCI Task runner. This is
+ in order to begin to decompose service instantiation in preparation for
+ Technology Profile work.
+
+ Once technology profiles are ready, some of this task may hang around or
+ be moved into OpenOMCI if there are any very common settings/configs to do
+ for any profile that may be provided in the v2.0 release
+
+ Currently, the only service tech profiles expected by v2.0 will be for AT&T
+ residential data service and DT residential data service.
+ """
+ task_priority = Task.DEFAULT_PRIORITY + 10
+ default_tpid = 0x8100
+ default_gem_payload = 1518
+ BRDCM_DEFAULT_VLAN = 4091
+
+ name = "ADTRAN MIB Install Flow Task"
+
+ def __init__(self, omci_agent, handler, match, action, is_upstream):
+ """
+ Class initialization
+
+ :param omci_agent: (OmciAdapterAgent) OMCI Adapter agent
+ :param handler: (AdtranOnuHandler) ONU Handler
+ :param match: (dict) Flow match rules
+ :param action: (dict) Flow action rules
+ :param is_upstream: (bool) True if upstream flow is being installed
+ """
+ super(AdtnInstallFlowTask, self).__init__(AdtnInstallFlowTask.name,
+ omci_agent,
+ handler.device_id,
+ priority=AdtnInstallFlowTask.task_priority)
+ self._handler = handler
+ self._onu_device = omci_agent.get_device(handler.device_id)
+ self._local_deferred = None
+
+ self._match = match
+ self._action = action
+
+ # TODO: Cleanup below that is not needed
+ self._vlan_tcis_1 = 0x900
+ self._input_tpid = AdtnInstallFlowTask.default_tpid
+ self._output_tpid = AdtnInstallFlowTask.default_tpid
+
+ if self._handler.xpon_support:
+ device = self._handler.adapter_agent.get_device(self.device_id)
+ self._cvid = device.vlan
+ else:
+ # TODO: TCIS below is just a test, may need 0x900...as in the xPON mode
+ self._vlan_tcis_1 = AdtnInstallFlowTask.BRDCM_DEFAULT_VLAN
+ self._cvid = AdtnInstallFlowTask.BRDCM_DEFAULT_VLAN
+
+ # Entity IDs. IDs with values can probably be most anything for most ONUs,
+ # IDs set to None are discovered/set
+ #
+ # TODO: Probably need to store many of these in the appropriate object (UNI, PON,...)
+ #
+ self._ieee_mapper_service_profile_entity_id = 0x100
+ self._gal_enet_profile_entity_id = 0x100
+
+ # Next to are specific
+ self._ethernet_uni_entity_id = self._handler.uni_ports[0].entity_id
+ self._vlan_config_entity_id = self._vlan_tcis_1
+
+ def cancel_deferred(self):
+ super(AdtnInstallFlowTask, self).cancel_deferred()
+
+ d, self._local_deferred = self._local_deferred, None
+ try:
+ if d is not None and not d.called:
+ d.cancel()
+ except:
+ pass
+
+ def start(self):
+ """
+ Start the flow installation
+ """
+ super(AdtnInstallFlowTask, self).start()
+ self._local_deferred = reactor.callLater(0, self.perform_flow_install)
+
+ def stop(self):
+ """
+ Shutdown flow install task
+ """
+ self.log.debug('stopping')
+
+ self.cancel_deferred()
+ super(AdtnInstallFlowTask, self).stop()
+
+ def check_status_and_state(self, results, operation=''):
+ """
+ Check the results of an OMCI response. An exception is thrown
+ if the task was cancelled or an error was detected.
+
+ :param results: (OmciFrame) OMCI Response frame
+ :param operation: (str) what operation was being performed
+ :return: True if successful, False if the entity existed (already created)
+ """
+ omci_msg = results.fields['omci_message'].fields
+ status = omci_msg['success_code']
+ error_mask = omci_msg.get('parameter_error_attributes_mask', 'n/a')
+ failed_mask = omci_msg.get('failed_attributes_mask', 'n/a')
+ unsupported_mask = omci_msg.get('unsupported_attributes_mask', 'n/a')
+
+ self.log.debug(operation, status=status, error_mask=error_mask,
+ failed_mask=failed_mask, unsupported_mask=unsupported_mask)
+
+ if status == RC.Success:
+ self.strobe_watchdog()
+ return True
+
+ elif status == RC.InstanceExists:
+ return False
+
+ raise ServiceDownloadFailure('{} failed with a status of {}, error_mask: {}, failed_mask: {}, unsupported_mask: {}'
+ .format(operation, status, error_mask, failed_mask, unsupported_mask))
+
+ @inlineCallbacks
+ def perform_flow_install(self):
+ """
+ Send the commands to configure the flow
+ """
+ self.log.info('perform-service-download')
+
+ 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
+ len(self._handler.pon_port.tconts) and
+ len(self._handler.pon_port.gem_ports) and
+ self._action.get('set_vlan_vid') is not None)
+
+ if self._handler.enabled and resources_available():
+ omci = self._onu_device.omci_cc
+ try:
+ # TODO: make this a member of the onu gem port or the uni port
+ _mac_bridge_service_profile_entity_id = 0x201
+ _mac_bridge_port_ani_entity_id = 0x2102 # TODO: can we just use the entity id from the anis list?
+ _set_vlan_vid = self._action['set_vlan_vid']
+
+ # Delete bridge ani side vlan filter
+ msg = VlanTaggingFilterDataFrame(_mac_bridge_port_ani_entity_id)
+ frame = msg.delete()
+
+ results = yield omci.send(frame)
+ self.check_status_and_state(results, 'flow-delete-vlan-tagging-filter-data')
+
+ # Re-Create bridge ani side vlan filter
+ msg = VlanTaggingFilterDataFrame(
+ _mac_bridge_port_ani_entity_id, # Entity ID
+ vlan_tcis=[_set_vlan_vid], # VLAN IDs
+ forward_operation=0x10
+ )
+ frame = msg.create()
+
+ results = yield omci.send(frame)
+ self.check_status_and_state(results, 'flow-create-vlan-tagging-filter-data')
+
+ # Update uni side extended vlan filter
+ # filter for untagged
+ # probably for eapol
+ # TODO: magic 0x1000 / 4096?
+ # TODO: lots of magic
+ attributes = dict(
+ received_frame_vlan_tagging_operation_table=
+ VlanTaggingOperation(
+ filter_outer_priority=15,
+ filter_outer_vid=4096,
+ filter_outer_tpid_de=0,
+
+ filter_inner_priority=15,
+ filter_inner_vid=4096,
+ filter_inner_tpid_de=0,
+ filter_ether_type=0,
+
+ treatment_tags_to_remove=0,
+ treatment_outer_priority=15,
+ treatment_outer_vid=0,
+ treatment_outer_tpid_de=0,
+
+ treatment_inner_priority=0,
+ treatment_inner_vid=_set_vlan_vid,
+ treatment_inner_tpid_de=4
+ )
+ )
+ # TODO: Move this to a task
+ msg = ExtendedVlanTaggingOperationConfigurationDataFrame(
+ _mac_bridge_service_profile_entity_id, # Bridge Entity ID
+ attributes=attributes # See above
+ )
+ frame = msg.set()
+
+ results = yield omci.send(frame)
+ self.check_status_and_state(results,
+ 'flow-set-ext-vlan-tagging-op-config-data-untagged')
+
+ # Update uni side extended vlan filter
+ # filter for vlan 0
+ # TODO: lots of magic
+ attributes = dict(
+ received_frame_vlan_tagging_operation_table=
+ VlanTaggingOperation(
+ filter_outer_priority=15,
+ # This entry is not a double-tag rule
+ filter_outer_vid=4096, # Do not filter on the outer VID value
+ filter_outer_tpid_de=0,
+ # Do not filter on the outer TPID field
+
+ filter_inner_priority=8, # Filter on inner vlan
+ filter_inner_vid=0x0, # Look for vlan 0
+ filter_inner_tpid_de=0, # Do not filter on inner TPID field
+ filter_ether_type=0, # Do not filter on EtherType
+
+ treatment_tags_to_remove=1,
+ treatment_outer_priority=15,
+ treatment_outer_vid=0,
+ treatment_outer_tpid_de=0,
+
+ treatment_inner_priority=8,
+ # Add an inner tag and insert this value as the priority
+ treatment_inner_vid=_set_vlan_vid,
+ # use this value as the VID in the inner VLAN tag
+ treatment_inner_tpid_de=4, # set TPID
+ )
+ )
+ msg = ExtendedVlanTaggingOperationConfigurationDataFrame(
+ _mac_bridge_service_profile_entity_id, # Bridge Entity ID
+ attributes=attributes # See above
+ )
+ frame = msg.set()
+
+ results = yield omci.send(frame)
+ self.check_status_and_state(results,
+ 'flow-set-ext-vlan-tagging-op-config-data-zero-tagged')
+ self.deferred.callback('flow-install-success')
+
+ except Exception as e:
+ # TODO: Better context info for this exception output...
+ self.log.exception('failed-to-install-flow', e=e)
+ self.deferred.errback(failure.Failure(e))
+
+ else:
+ # TODO: Provide better error reason, what was missing...
+ e = ServiceResourcesFailure('Required resources are not available')
+ self.deferred.errback(failure.Failure(e))
+
+ def check_status_and_state(self, result, operation=''):
+ from voltha.extensions.omci.omci_defs import ReasonCodes
+ self.log.debug('function-entry')
+ omci_msg = result.fields['omci_message'].fields
+ status = omci_msg['success_code']
+ error_mask = omci_msg.get('parameter_error_attributes_mask', 'n/a')
+ failed_mask = omci_msg.get('failed_attributes_mask', 'n/a')
+ unsupported_mask = omci_msg.get('unsupported_attributes_mask', 'n/a')
+
+ self.log.debug("OMCI Result:", operation, omci_msg=omci_msg, status=status,
+ error_mask=error_mask,
+ failed_mask=failed_mask, unsupported_mask=unsupported_mask)
+
+ if status == ReasonCodes.Success:
+ return True
+
+ elif status == ReasonCodes.InstanceExists:
+ return False
diff --git a/voltha/adapters/adtran_onu/omci/adtn_mib_download_task.py b/voltha/adapters/adtran_onu/omci/adtn_mib_download_task.py
index e782913..6c853e1 100644
--- a/voltha/adapters/adtran_onu/omci/adtn_mib_download_task.py
+++ b/voltha/adapters/adtran_onu/omci/adtn_mib_download_task.py
@@ -80,15 +80,12 @@
self._uni_port_num = 0 # TODO Both port numbers are the same, is this correct? See MacBridgePortConfigurationDataFrame
self._vlan_tcis_1 = 0x900
- self._input_tpid = AdtnMibDownloadTask.default_tpid
- self._output_tpid = AdtnMibDownloadTask.default_tpid
-
- device = self._handler.adapter_agent.get_device(self.device_id)
- self._cvid = device.vlan
# Entity IDs. IDs with values can probably be most anything for most ONUs,
# IDs set to None are discovered/set
-
+ #
+ # TODO: Probably need to store many of these in the appropriate object (UNI, PON,...)
+ #
self._mac_bridge_service_profile_entity_id = 0x100
self._ieee_mapper_service_profile_entity_id = 0x100
self._mac_bridge_port_ani_entity_id = 0x100
@@ -157,20 +154,18 @@
"""
Send the commands to minimally configure the PON, Bridge, and
UNI ports for this device. The application of any service flows
- and other characteristics are done as needed.
+ and other characteristics are done once resources (gem-ports, tconts, ...)
+ have been defined.
"""
- self.log.info('perform-download')
+ self.log.info('perform-initial-download')
device = self._handler.adapter_agent.get_device(self.device_id)
def resources_available():
- return (device.vlan > 0 and
- len(self._handler.uni_ports) > 0 and
- len(self._handler.pon_port.tconts) and
- len(self._handler.pon_port.gem_ports))
+ return len(self._handler.uni_ports) > 0
if self._handler.enabled and resources_available():
- device.reason = 'Performing OMCI Download'
+ device.reason = 'Performing Initial OMCI Download'
self._handler.adapter_agent.update_device(device)
try:
@@ -182,16 +177,10 @@
# Provision the initial bridge configuration
yield self.perform_initial_bridge_setup()
- # And not all the service specific work
- yield self.perform_service_specific_steps()
-
- # And re-enable the UNIs if needed
- yield self.enable_unis(self._handler.uni_ports, False)
-
# If here, we are done
device = self._handler.adapter_agent.get_device(self.device_id)
- device.reason = ''
+ device.reason = 'Initial OMCI Download Complete'
self._handler.adapter_agent.update_device(device)
self.deferred.callback('TODO: What should we return to the caller?')
@@ -200,7 +189,7 @@
else:
# TODO: Provide better error reason, what was missing...
- e = MibResourcesFailure('Required resources are not available')
+ e = MibResourcesFailure('ONU is not enabled')
self.deferred.errback(failure.Failure(e))
@inlineCallbacks
@@ -336,215 +325,6 @@
returnValue(None)
@inlineCallbacks
- def perform_service_specific_steps(self):
- omci_cc = self._onu_device.omci_cc
- frame = None
-
- try:
- ################################################################################
- # TCONTS
- #
- # EntityID will be referenced by:
- # - GemPortNetworkCtp
- # References:
- # - ONU created TCONT (created on ONU startup)
-
- tcont_idents = self._onu_device.query_mib(Tcont.class_id)
- self.log.debug('tcont-idents', tcont_idents=tcont_idents)
-
- for tcont in self._handler.pon_port.tconts.itervalues():
- free_entity_id = next((k for k, v in tcont_idents.items()
- if isinstance(k, int) and
- v.get('attributes', {}).get('alloc_id', 0) == 0xFFFF), None)
- if free_entity_id is None:
- self.log.error('no-available-tconts')
- break
- # TODO: Need to restore on failure. Need to check status/results
- yield tcont.add_to_hardware(omci_cc, free_entity_id)
-
- ################################################################################
- # GEMS (GemPortNetworkCtp and GemInterworkingTp)
- #
- # For both of these MEs, the entity_id is the GEM Port ID. The entity id of the
- # GemInterworkingTp ME could be different since it has an attribute to specify
- # the GemPortNetworkCtp entity id.
- #
- # TODO: In the GEM Port routine to add, it has a hardcoded upstream TM ID of 0x8000
- # for the GemPortNetworkCtp ME
- #
- # GemPortNetworkCtp
- # EntityID will be referenced by:
- # - GemInterworkingTp
- # References:
- # - TCONT
- # - Hardcoded upstream TM Entity ID
- # - (Possibly in Future) Upstream Traffic descriptor profile pointer
- #
- # GemInterworkingTp
- # EntityID will be referenced by:
- # - Ieee8021pMapperServiceProfile
- # References:
- # - GemPortNetworkCtp
- # - Ieee8021pMapperServiceProfile
- # - GalEthernetProfile
- #
-
- for gem_port in self._handler.pon_port.gem_ports.itervalues():
- tcont = gem_port.tcont
- if tcont is None:
- self.log.error('unknown-tcont-reference', gem_id=gem_port.gem_id)
- continue
- # TODO: Need to restore on failure. Need to check status/results
- yield gem_port.add_to_hardware(omci_cc,
- tcont.entity_id,
- self._ieee_mapper_service_profile_entity_id,
- self._gal_enet_profile_entity_id)
-
- ################################################################################
- # Update the IEEE 802.1p Mapper Service Profile config
- #
- # EntityID was created prior to this call. This is a set
- #
- # References:
- # - Gem Interworking TPs are set here
- #
- # TODO: All p-bits currently go to the one and only GEMPORT ID for now
- gem_ports = self._handler.pon_port.gem_ports
- gem_entity_ids = [gem_port.entity_id for _, gem_port in gem_ports.items()] \
- if len(gem_ports) else [OmciNullPointer]
-
- frame = Ieee8021pMapperServiceProfileFrame(
- self._ieee_mapper_service_profile_entity_id, # 802.1p mapper Service Mapper Profile ID
- interwork_tp_pointers=gem_entity_ids # Interworking TP IDs
- ).set()
- results = yield omci_cc.send(frame)
- self.check_status_and_state(results, 'set-8021p-mapper-service-profile')
-
- ################################################################################
- # Create Extended VLAN Tagging Operation config (PON-side)
- #
- # EntityID relates to the VLAN TCIS
- # References:
- # - VLAN TCIS from previously created VLAN Tagging filter data
- # - PPTP Ethernet UNI
- #
- # TODO: add entry here for additional UNI interfaces
-
- attributes = dict(
- association_type=2, # Assoc Type, PPTP Ethernet UNI
- associated_me_pointer=self._ethernet_uni_entity_id # Assoc ME, PPTP Entity Id
- )
-
- frame = ExtendedVlanTaggingOperationConfigurationDataFrame(
- self._vlan_config_entity_id,
- attributes=attributes
- ).create()
- results = yield omci_cc.send(frame)
- self.check_status_and_state(results, 'create-extended-vlan-tagging-operation-configuration-data')
-
- ################################################################################
- # Update Extended VLAN Tagging Operation Config Data
- #
- # Specifies the TPIDs in use and that operations in the downstream direction are
- # inverse to the operations in the upstream direction
- # TODO: Downstream mode may need to be modified once we work more on the flow rules
-
- attributes = dict(
- input_tpid=self._input_tpid, # input TPID
- output_tpid=self._output_tpid, # output TPID
- downstream_mode=0, # inverse of upstream
- )
- frame = ExtendedVlanTaggingOperationConfigurationDataFrame(
- self._vlan_config_entity_id,
- attributes=attributes
- ).set()
- results = yield omci_cc.send(frame)
- self.check_status_and_state(results, 'set-extended-vlan-tagging-operation-configuration-data')
-
- ################################################################################
- # Update Extended VLAN Tagging Operation Config Data
- #
- # parameters: Entity Id ( 0x900), Filter Inner Vlan Id(0x1000-4096,do not filter on Inner vid,
- # Treatment Inner Vlan Id : 2
-
- attributes = dict(
- received_frame_vlan_tagging_operation_table=
- VlanTaggingOperation(
- filter_outer_priority=15, # This entry is not a double-tag rule
- filter_outer_vid=4096, # Do not filter on the outer VID value
- filter_outer_tpid_de=0, # Do not filter on the outer TPID field
-
- filter_inner_priority=15, # This is a no-tag rule, ignore all other VLAN tag filter fields
- filter_inner_vid=0x1000, # Do not filter on the inner VID
- filter_inner_tpid_de=0, # Do not filter on inner TPID field
-
- filter_ether_type=0, # Do not filter on EtherType
- treatment_tags_to_remove=0, # Remove 0 tags
-
- treatment_outer_priority=15, # Do not add an outer tag
- treatment_outer_vid=0, # n/a
- treatment_outer_tpid_de=0, # n/a
-
- treatment_inner_priority=0, # Add an inner tag and insert this value as the priority
- treatment_inner_vid=self._cvid, # use this value as the VID in the inner VLAN tag
- treatment_inner_tpid_de=4, # set TPID
- )
- )
- frame = ExtendedVlanTaggingOperationConfigurationDataFrame(
- self._vlan_config_entity_id, # Entity ID
- attributes=attributes # See above
- ).set()
- results = yield omci_cc.send(frame)
- self.check_status_and_state(results, 'set-extended-vlan-tagging-operation-configuration-data-untagged')
-
- ################################################################################
- ################################################################################
- ################################################################################
- # BP: This is for AT&T RG's #
- # TODO: CB: NOTE: TRY THIS ONCE OTHER SEQUENCES WORK
- #
- # Set AR - ExtendedVlanTaggingOperationConfigData
- # 514 - RxVlanTaggingOperationTable - add VLAN <cvid> to priority tagged pkts - c-vid
- # results = yield omci.send_set_extended_vlan_tagging_operation_vlan_configuration_data_single_tag(
- # 0x900, # Entity ID
- # 8, # Filter Inner Priority, do not filter on Inner Priority
- # 0, # Filter Inner VID, this will be 0 in CORD
- # 0, # Filter Inner TPID DE
- # 1, # Treatment tags, number of tags to remove
- # 8, # Treatment inner priority, copy Inner Priority
- # 2) # Treatment inner VID, this will be 2 in CORD
- # Set AR - ExtendedVlanTaggingOperationConfigData
- # 514 - RxVlanTaggingOperationTable - add VLAN <cvid> to priority tagged pkts - c-vid
- # results = yield omci.send_set_extended_vlan_tagging_operation_vlan_configuration_data_single_tag(
- # 0x200, # Entity ID
- # 8, # Filter Inner Priority
- # 0, # Filter Inner VID
- # 0, # Filter Inner TPID DE
- # 1, # Treatment tags to remove
- # 8, # Treatment inner priority
- # cvid) # Treatment inner VID
- # Set AR - ExtendedVlanTaggingOperationConfigData
- # 514 - RxVlanTaggingOperationTable - add VLAN <cvid> to untagged pkts - c-vid
- # results = yield omci.send_set_extended_vlan_tagging_operation_vlan_configuration_data_untagged(
- # 0x100, # Entity ID BP: Oldvalue 0x202
- # 0x1000, # Filter Inner VID BP: Oldvalue 0x1000
- # cvid) # Treatment inner VID BP: cvid
- # success = results.fields['omci_message'].fields['success_code'] == 0
- # error_mask = results.fields['omci_message'].fields['parameter_error_attributes_mask']
-
- ###############################################################################
-
- except TimeoutError as e:
- self.log.warn('rx-timeout-2', frame=frame)
- raise
-
- except Exception as e:
- self.log.exception('omci-setup-2', e=e)
- raise
-
- returnValue(None)
-
- @inlineCallbacks
def enable_unis(self, unis, force_lock):
"""
Lock or unlock one or more UNI 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
new file mode 100644
index 0000000..e62cb6e
--- /dev/null
+++ b/voltha/adapters/adtran_onu/omci/adtn_service_download_task.py
@@ -0,0 +1,459 @@
+#
+# 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 twisted.internet import reactor
+from twisted.internet.defer import inlineCallbacks, returnValue, TimeoutError, failure
+from voltha.extensions.omci.omci_me import *
+from voltha.extensions.omci.tasks.task import Task
+from voltha.extensions.omci.omci_defs import *
+
+OP = EntityOperations
+RC = ReasonCodes
+
+
+class ServiceDownloadFailure(Exception):
+ """
+ This error is raised by default when the download fails
+ """
+
+
+class ServiceResourcesFailure(Exception):
+ """
+ This error is raised by when one or more resources required is not available
+ """
+
+
+class AdtnServiceDownloadTask(Task):
+ """
+ OpenOMCI MIB Download Example - Service specific
+
+ This task takes the legacy OMCI 'script' for provisioning the Adtran ONU
+ and converts it to run as a Task on the OpenOMCI Task runner. This is
+ in order to begin to decompose service instantiation in preparation for
+ Technology Profile work.
+
+ Once technology profiles are ready, some of this task may hang around or
+ be moved into OpenOMCI if there are any very common settings/configs to do
+ for any profile that may be provided in the v2.0 release
+
+ Currently, the only service tech profiles expected by v2.0 will be for AT&T
+ residential data service and DT residential data service.
+ """
+ task_priority = Task.DEFAULT_PRIORITY + 10
+ default_tpid = 0x8100
+ default_gem_payload = 1518
+ BRDCM_DEFAULT_VLAN = 4091
+
+ name = "ADTRAN MIB Download Example Task"
+
+ def __init__(self, omci_agent, handler):
+ """
+ Class initialization
+
+ :param omci_agent: (OmciAdapterAgent) OMCI Adapter agent
+ :param device_id: (str) ONU Device ID
+ """
+ super(AdtnServiceDownloadTask, self).__init__(AdtnServiceDownloadTask.name,
+ omci_agent,
+ handler.device_id,
+ priority=AdtnServiceDownloadTask.task_priority)
+ self._handler = handler
+ self._onu_device = omci_agent.get_device(handler.device_id)
+ self._local_deferred = None
+
+ self._vlan_tcis_1 = 0x900
+ 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._cvid = device.vlan
+ else:
+ # TODO: TCIS below is just a test, may need 0x900...as in the xPON mode
+ self._vlan_tcis_1 = AdtnServiceDownloadTask.BRDCM_DEFAULT_VLAN
+ self._cvid = AdtnServiceDownloadTask.BRDCM_DEFAULT_VLAN
+
+ # Entity IDs. IDs with values can probably be most anything for most ONUs,
+ # IDs set to None are discovered/set
+ #
+ # TODO: Probably need to store many of these in the appropriate object (UNI, PON,...)
+ #
+ self._ieee_mapper_service_profile_entity_id = 0x100
+ self._gal_enet_profile_entity_id = 0x100
+
+ # Next to are specific
+ self._ethernet_uni_entity_id = self._handler.uni_ports[0].entity_id
+ self._vlan_config_entity_id = self._vlan_tcis_1
+
+ def cancel_deferred(self):
+ super(AdtnServiceDownloadTask, self).cancel_deferred()
+
+ d, self._local_deferred = self._local_deferred, None
+ try:
+ if d is not None and not d.called:
+ d.cancel()
+ except:
+ pass
+
+ def start(self):
+ """
+ Start the MIB Service Download
+ """
+ super(AdtnServiceDownloadTask, self).start()
+ self._local_deferred = reactor.callLater(0, self.perform_service_download)
+
+ def stop(self):
+ """
+ Shutdown MIB Service download
+ """
+ self.log.debug('stopping')
+
+ self.cancel_deferred()
+ super(AdtnServiceDownloadTask, self).stop()
+
+ def check_status_and_state(self, results, operation=''):
+ """
+ Check the results of an OMCI response. An exception is thrown
+ if the task was cancelled or an error was detected.
+
+ :param results: (OmciFrame) OMCI Response frame
+ :param operation: (str) what operation was being performed
+ :return: True if successful, False if the entity existed (already created)
+ """
+ omci_msg = results.fields['omci_message'].fields
+ status = omci_msg['success_code']
+ error_mask = omci_msg.get('parameter_error_attributes_mask', 'n/a')
+ failed_mask = omci_msg.get('failed_attributes_mask', 'n/a')
+ unsupported_mask = omci_msg.get('unsupported_attributes_mask', 'n/a')
+
+ self.log.debug(operation, status=status, error_mask=error_mask,
+ failed_mask=failed_mask, unsupported_mask=unsupported_mask)
+
+ if status == RC.Success:
+ self.strobe_watchdog()
+ return True
+
+ elif status == RC.InstanceExists:
+ return False
+
+ raise ServiceDownloadFailure('{} failed with a status of {}, error_mask: {}, failed_mask: {}, unsupported_mask: {}'
+ .format(operation, status, error_mask, failed_mask, unsupported_mask))
+
+ @inlineCallbacks
+ def perform_service_download(self):
+ """
+ Send the commands to minimally configure the PON, Bridge, and
+ UNI ports for this device. The application of any service flows
+ and other characteristics are done once resources (gem-ports, tconts, ...)
+ have been defined.
+ """
+ self.log.info('perform-service-download')
+
+ 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._handler.pon_port.tconts) and
+ len(self._handler.pon_port.gem_ports))
+ else:
+ return (len(self._handler.uni_ports) > 0 and
+ len(self._handler.pon_port.tconts) and
+ len(self._handler.pon_port.gem_ports))
+
+ if self._handler.enabled and resources_available():
+ device.reason = 'Performing Service OMCI Download'
+ self._handler.adapter_agent.update_device(device)
+
+ try:
+ # Lock the UNI ports to prevent any alarms during initial configuration
+ # of the ONU
+ self.strobe_watchdog()
+ # Provision the initial bridge configuration
+ yield self.perform_service_specific_steps()
+
+ # And re-enable the UNIs if needed
+ yield self.enable_unis(self._handler.uni_ports, False)
+
+ # If here, we are done
+ device = self._handler.adapter_agent.get_device(self.device_id)
+
+ device.reason = ''
+ self._handler.adapter_agent.update_device(device)
+ self.deferred.callback('service-download-success')
+
+ except TimeoutError as e:
+ self.deferred.errback(failure.Failure(e))
+
+ else:
+ # TODO: Provide better error reason, what was missing...
+ e = ServiceResourcesFailure('Required resources are not available')
+ self.deferred.errback(failure.Failure(e))
+
+ @inlineCallbacks
+ def perform_service_specific_steps(self):
+ omci_cc = self._onu_device.omci_cc
+ frame = None
+
+ try:
+ ################################################################################
+ # TCONTS
+ #
+ # EntityID will be referenced by:
+ # - GemPortNetworkCtp
+ # References:
+ # - ONU created TCONT (created on ONU startup)
+
+ tcont_idents = self._onu_device.query_mib(Tcont.class_id)
+ self.log.debug('tcont-idents', tcont_idents=tcont_idents)
+
+ for tcont in self._handler.pon_port.tconts.itervalues():
+ free_entity_id = next((k for k, v in tcont_idents.items()
+ if isinstance(k, int) and
+ v.get('attributes', {}).get('alloc_id', 0) == 0xFFFF), None)
+ if free_entity_id is None:
+ self.log.error('no-available-tconts')
+ break
+ # TODO: Need to restore on failure. Need to check status/results
+ yield tcont.add_to_hardware(omci_cc, free_entity_id)
+
+ ################################################################################
+ # GEMS (GemPortNetworkCtp and GemInterworkingTp)
+ #
+ # For both of these MEs, the entity_id is the GEM Port ID. The entity id of the
+ # GemInterworkingTp ME could be different since it has an attribute to specify
+ # the GemPortNetworkCtp entity id.
+ #
+ # TODO: In the GEM Port routine to add, it has a hardcoded upstream TM ID of 0x8000
+ # for the GemPortNetworkCtp ME
+ #
+ # GemPortNetworkCtp
+ # EntityID will be referenced by:
+ # - GemInterworkingTp
+ # References:
+ # - TCONT
+ # - Hardcoded upstream TM Entity ID
+ # - (Possibly in Future) Upstream Traffic descriptor profile pointer
+ #
+ # GemInterworkingTp
+ # EntityID will be referenced by:
+ # - Ieee8021pMapperServiceProfile
+ # References:
+ # - GemPortNetworkCtp
+ # - Ieee8021pMapperServiceProfile
+ # - GalEthernetProfile
+ #
+
+ for gem_port in self._handler.pon_port.gem_ports.itervalues():
+ tcont = gem_port.tcont
+ if tcont is None:
+ self.log.error('unknown-tcont-reference', gem_id=gem_port.gem_id)
+ continue
+ # TODO: Need to restore on failure. Need to check status/results
+ yield gem_port.add_to_hardware(omci_cc,
+ tcont.entity_id,
+ self._ieee_mapper_service_profile_entity_id,
+ self._gal_enet_profile_entity_id)
+
+ ################################################################################
+ # Update the IEEE 802.1p Mapper Service Profile config
+ #
+ # EntityID was created prior to this call. This is a set
+ #
+ # References:
+ # - Gem Interworking TPs are set here
+ #
+ # TODO: All p-bits currently go to the one and only GEMPORT ID for now
+ gem_ports = self._handler.pon_port.gem_ports
+ gem_entity_ids = [gem_port.entity_id for _, gem_port in gem_ports.items()] \
+ if len(gem_ports) else [OmciNullPointer]
+
+ frame = Ieee8021pMapperServiceProfileFrame(
+ self._ieee_mapper_service_profile_entity_id, # 802.1p mapper Service Mapper Profile ID
+ interwork_tp_pointers=gem_entity_ids # Interworking TP IDs
+ ).set()
+ results = yield omci_cc.send(frame)
+ self.check_status_and_state(results, 'set-8021p-mapper-service-profile')
+
+ ################################################################################
+ # Create Extended VLAN Tagging Operation config (PON-side)
+ #
+ # EntityID relates to the VLAN TCIS
+ # References:
+ # - VLAN TCIS from previously created VLAN Tagging filter data
+ # - PPTP Ethernet UNI
+ #
+ # TODO: add entry here for additional UNI interfaces
+
+ attributes = dict(
+ association_type=2, # Assoc Type, PPTP Ethernet UNI
+ associated_me_pointer=self._ethernet_uni_entity_id # Assoc ME, PPTP Entity Id
+ )
+
+ frame = ExtendedVlanTaggingOperationConfigurationDataFrame(
+ self._vlan_config_entity_id,
+ attributes=attributes
+ ).create()
+ results = yield omci_cc.send(frame)
+ self.check_status_and_state(results, 'create-extended-vlan-tagging-operation-configuration-data')
+
+ ################################################################################
+ # Update Extended VLAN Tagging Operation Config Data
+ #
+ # Specifies the TPIDs in use and that operations in the downstream direction are
+ # inverse to the operations in the upstream direction
+ # TODO: Downstream mode may need to be modified once we work more on the flow rules
+
+ attributes = dict(
+ input_tpid=self._input_tpid, # input TPID
+ output_tpid=self._output_tpid, # output TPID
+ downstream_mode=0, # inverse of upstream
+ )
+ frame = ExtendedVlanTaggingOperationConfigurationDataFrame(
+ self._vlan_config_entity_id,
+ attributes=attributes
+ ).set()
+ results = yield omci_cc.send(frame)
+ self.check_status_and_state(results, 'set-extended-vlan-tagging-operation-configuration-data')
+
+ ################################################################################
+ # Update Extended VLAN Tagging Operation Config Data
+ #
+ # parameters: Entity Id ( 0x900), Filter Inner Vlan Id(0x1000-4096,do not filter on Inner vid,
+ # Treatment Inner Vlan Id : 2
+
+ attributes = dict(
+ received_frame_vlan_tagging_operation_table=
+ VlanTaggingOperation(
+ filter_outer_priority=15, # This entry is not a double-tag rule
+ filter_outer_vid=4096, # Do not filter on the outer VID value
+ filter_outer_tpid_de=0, # Do not filter on the outer TPID field
+
+ filter_inner_priority=15, # This is a no-tag rule, ignore all other VLAN tag filter fields
+ filter_inner_vid=0x1000, # Do not filter on the inner VID
+ filter_inner_tpid_de=0, # Do not filter on inner TPID field
+
+ filter_ether_type=0, # Do not filter on EtherType
+ treatment_tags_to_remove=0, # Remove 0 tags
+
+ treatment_outer_priority=15, # Do not add an outer tag
+ treatment_outer_vid=0, # n/a
+ treatment_outer_tpid_de=0, # n/a
+
+ treatment_inner_priority=0, # Add an inner tag and insert this value as the priority
+ treatment_inner_vid=self._cvid, # use this value as the VID in the inner VLAN tag
+ treatment_inner_tpid_de=4, # set TPID
+ )
+ )
+ frame = ExtendedVlanTaggingOperationConfigurationDataFrame(
+ self._vlan_config_entity_id, # Entity ID
+ attributes=attributes # See above
+ ).set()
+ results = yield omci_cc.send(frame)
+ self.check_status_and_state(results, 'set-extended-vlan-tagging-operation-configuration-data-untagged')
+
+ ################################################################################
+ ################################################################################
+ ################################################################################
+ # BP: This is for AT&T RG's #
+ # TODO: CB: NOTE: TRY THIS ONCE OTHER SEQUENCES WORK
+ #
+ # Set AR - ExtendedVlanTaggingOperationConfigData
+ # 514 - RxVlanTaggingOperationTable - add VLAN <cvid> to priority tagged pkts - c-vid
+ # results = yield omci.send_set_extended_vlan_tagging_operation_vlan_configuration_data_single_tag(
+ # 0x900, # Entity ID
+ # 8, # Filter Inner Priority, do not filter on Inner Priority
+ # 0, # Filter Inner VID, this will be 0 in CORD
+ # 0, # Filter Inner TPID DE
+ # 1, # Treatment tags, number of tags to remove
+ # 8, # Treatment inner priority, copy Inner Priority
+ # 2) # Treatment inner VID, this will be 2 in CORD
+ # Set AR - ExtendedVlanTaggingOperationConfigData
+ # 514 - RxVlanTaggingOperationTable - add VLAN <cvid> to priority tagged pkts - c-vid
+ # results = yield omci.send_set_extended_vlan_tagging_operation_vlan_configuration_data_single_tag(
+ # 0x200, # Entity ID
+ # 8, # Filter Inner Priority
+ # 0, # Filter Inner VID
+ # 0, # Filter Inner TPID DE
+ # 1, # Treatment tags to remove
+ # 8, # Treatment inner priority
+ # cvid) # Treatment inner VID
+ # Set AR - ExtendedVlanTaggingOperationConfigData
+ # 514 - RxVlanTaggingOperationTable - add VLAN <cvid> to untagged pkts - c-vid
+ # results = yield omci.send_set_extended_vlan_tagging_operation_vlan_configuration_data_untagged(
+ # 0x100, # Entity ID BP: Oldvalue 0x202
+ # 0x1000, # Filter Inner VID BP: Oldvalue 0x1000
+ # cvid) # Treatment inner VID BP: cvid
+ # success = results.fields['omci_message'].fields['success_code'] == 0
+ # error_mask = results.fields['omci_message'].fields['parameter_error_attributes_mask']
+
+ ###############################################################################
+
+ except TimeoutError as e:
+ self.log.warn('rx-timeout-2', frame=frame)
+ raise
+
+ except Exception as e:
+ self.log.exception('omci-setup-2', e=e)
+ raise
+
+ returnValue(None)
+
+ @inlineCallbacks
+ def enable_unis(self, unis, force_lock):
+ """
+ Lock or unlock one or more UNI ports
+
+ :param unis: (list) of UNI objects
+ :param force_lock: (boolean) If True, force lock regardless of enabled state
+ """
+ omci_cc = self._onu_device.omci_cc
+ frame = None
+
+ for uni in unis:
+ ################################################################################
+ # Lock/Unlock UNI - 0 to Unlock, 1 to lock
+ #
+ # EntityID is referenced by:
+ # - MAC bridge port configuration data for the UNI side
+ # References:
+ # - Nothing
+ try:
+ state = 1 if force_lock or not uni.enabled else 0
+ frame = PptpEthernetUniFrame(uni.entity_id,
+ attributes=dict(administrative_state=state)).set()
+ results = yield omci_cc.send(frame)
+ self.check_status_and_state(results, 'set-pptp-ethernet-uni-lock-restore')
+
+ except TimeoutError:
+ self.log.warn('rx-timeout', frame=frame)
+ raise
+
+ except Exception as e:
+ self.log.exception('omci-failure', e=e)
+ raise
+
+ returnValue(None)
+
+
+
+
+
+
+
+
+
diff --git a/voltha/adapters/adtran_onu/omci/omci.py b/voltha/adapters/adtran_onu/omci/omci.py
index 2b008a5..9e1ba37 100644
--- a/voltha/adapters/adtran_onu/omci/omci.py
+++ b/voltha/adapters/adtran_onu/omci/omci.py
@@ -52,6 +52,8 @@
self._connectivity_subscription = None
self._capabilities_subscription = None
+ self._service_downloaded = False
+ self._mib_downloaded = False
self._mib_download_task = None
self._mib_download_deferred = None
@@ -94,6 +96,7 @@
self._unsubscribe_to_events()
self._onu_omci_device.stop() # Will also cancel any running tasks/state-machines
+ self._mib_downloaded = False
self._mib_download_task = None
self._bridge_initialized = False
self._in_sync_reached = False
@@ -198,11 +201,15 @@
# Save entity_id of PON ports
self._handler.pon_ports[0].entity_id = ani_g.keys()[0]
- # Save entity_id for UNI ports
- 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)
+ # 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
@@ -321,8 +328,16 @@
"""
if self._capabilities_subscription is not None:
from adtn_mib_download_task import AdtnMibDownloadTask
+ from adtn_service_download_task import AdtnServiceDownloadTask
def success(_results):
+ if self._mib_downloaded:
+ self._service_downloaded = True
+ else:
+ # Now try the services
+ self._mib_downloaded = True
+ reactor.callLater(0, self.capabilities_handler)
+
self._mib_download_task = None
def failure(_reason):
@@ -330,7 +345,11 @@
# TODO: Handle failure, retry for now?
self._mib_download_deferred = reactor.callLater(_STARTUP_RETRY_WAIT,
self.capabilities_handler)
- self._mib_download_task = AdtnMibDownloadTask(self.omci_agent, self._handler)
+ if not self._mib_downloaded:
+ self._mib_download_task = AdtnMibDownloadTask(self.omci_agent, self._handler)
+ else:
+ self._mib_download_task = AdtnServiceDownloadTask(self.omci_agent, self._handler)
+
self._mib_download_deferred = self._onu_omci_device.task_runner.queue_task(self._mib_download_task)
self._mib_download_deferred.addCallbacks(success, failure)
diff --git a/voltha/adapters/adtran_onu/uni_port.py b/voltha/adapters/adtran_onu/uni_port.py
index 32636a2..0c53b2b 100644
--- a/voltha/adapters/adtran_onu/uni_port.py
+++ b/voltha/adapters/adtran_onu/uni_port.py
@@ -24,7 +24,7 @@
class UniPort(object):
"""Wraps southbound-port(s) support for ONU"""
- DEFAULT_UNTAGGED_VLAN = 4092
+ DEFAULT_UNTAGGED_VLAN = 4091 # TODO: BroadCom Default. Need a better way to define this
def __init__(self, handler, name, port_no, ofp_port_no, subscriber_vlan=None,
untagged_vlan=None):
@@ -40,6 +40,7 @@
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
@@ -110,6 +111,20 @@
return self._port_number
@property
+ def mac_bridge_port_num(self):
+ """
+ Port number used when creating MacBridgePortConfigurationDataFrame port number
+ :return: (int) port number
+ """
+ self.log.debug('function-entry')
+ return self._mac_bridge_port_num
+
+ @mac_bridge_port_num.setter
+ def mac_bridge_port_num(self, value):
+ self.log.debug('function-entry')
+ self._mac_bridge_port_num = value
+
+ @property
def entity_id(self):
"""
OMCI UNI_G entity ID for port
@@ -196,7 +211,7 @@
"""
if self._port is None:
self._port = Port(port_no=self.port_number,
- label='Ethernet port',
+ label='vEth-{}'.format(self.port_number),
type=Port.ETHERNET_UNI,
admin_state=self._admin_state,
oper_status=self._oper_status)