VOL-665: ADTRAN ONU support for OpenOMCI
Change-Id: I65bb69b7bd5e769b7fff123947391ddf87d6e7ce
diff --git a/voltha/adapters/adtran_onu/adtran_onu.py b/voltha/adapters/adtran_onu/adtran_onu.py
index b062aa0..d928395 100755
--- a/voltha/adapters/adtran_onu/adtran_onu.py
+++ b/voltha/adapters/adtran_onu/adtran_onu.py
@@ -17,10 +17,10 @@
"""
Adtran ONU adapter.
"""
+import structlog
import binascii
from voltha.adapters.iadapter import OnuAdapter
from voltha.protos import third_party
-from voltha.extensions.omci.omci import *
from adtran_onu_handler import AdtranOnuHandler
from twisted.internet import reactor
@@ -36,7 +36,7 @@
device_handler_class=AdtranOnuHandler,
name='adtran_onu',
vendor='Adtran, Inc.',
- version='0.5',
+ version='0.6',
device_type='adtran_onu',
vendor_id='ADTN')
@@ -76,7 +76,7 @@
def receive_inter_adapter_message(self, msg):
# Currently the only OLT Device adapter that uses this is the EdgeCore
- log.info('receive_inter_adapter_message', msg=msg)
+ self.log.info('receive_inter_adapter_message', msg=msg)
proxy_address = msg['proxy_address']
assert proxy_address is not None
@@ -100,7 +100,7 @@
raise NotImplementedError('Not an ONU method')
def receive_proxied_message(self, proxy_address, msg):
- log.debug('receive-proxied-message', proxy_address=proxy_address,
+ self.log.debug('receive-proxied-message', proxy_address=proxy_address,
device_id=proxy_address.device_id, msg=binascii.hexlify(msg))
# Device_id from the proxy_address is the olt device id. We need to
# get the onu device id using the port number in the proxy_address
diff --git a/voltha/adapters/adtran_onu/adtran_onu_handler.py b/voltha/adapters/adtran_onu/adtran_onu_handler.py
index dea1e78..60b4c69 100644
--- a/voltha/adapters/adtran_onu/adtran_onu_handler.py
+++ b/voltha/adapters/adtran_onu/adtran_onu_handler.py
@@ -15,10 +15,9 @@
#
import arrow
+import structlog
from voltha.adapters.adtran_olt.xpon.adtran_xpon import AdtranXPON
-from omci.omci_cc import OMCI_CC
-from omci.omci_entities import onu_custom_entity_classes
from pon_port import PonPort
from uni_port import UniPort
from heartbeat import HeartBeat
@@ -34,14 +33,15 @@
from voltha.protos import third_party
from voltha.protos.common_pb2 import OperStatus, ConnectStatus
from voltha.protos.device_pb2 import Image
-from voltha.extensions.omci.omci import *
from common.utils.indexpool import IndexPool
+from voltha.extensions.omci.openomci_agent import OpenOMCIAgent
_ = third_party
_MAXIMUM_PORT = 128 # PON and UNI ports
_ONU_REBOOT_MIN = 60
_ONU_REBOOT_RETRY = 10
+
class AdtranOnuHandler(AdtranXPON):
def __init__(self, adapter, device_id):
kwargs = dict()
@@ -60,12 +60,17 @@
self._upstream_channel_speed = 0
self._unis = dict() # Port # -> UniPort
- self._pons = dict() # Port # -> PonPort
+ self._pon = None
self._heartbeat = HeartBeat.create(self, device_id)
self._deferred = None
self._event_deferred = None
- self._omci = None
+
+ # TODO: Remove next two lines if/when OpenOMCI is in the core or a container
+ # in order to support multiple ONUs per instance
+ self._omci_agent = OpenOMCIAgent(self.adapter_agent.core)
+ self._omci_agent.start()
+
self._port_number_pool = IndexPool(_MAXIMUM_PORT, 1)
self._olt_created = False # True if deprecated method of OLT creating DA is used
@@ -128,8 +133,14 @@
return self._olt_created # ONU was created with deprecated 'child_device_detected' call
@property
+ def omci_agent(self):
+ return self._omci_agent
+
+ @property
def omci(self):
- return self._omci
+ # TODO: Decrement access to Communications channel at this point? What about current PM stuff?
+ _onu_omci_device = self._pon.onu_omci_device
+ return _onu_omci_device.omci_cc if _onu_omci_device is not None else None
@property
def heartbeat(self):
@@ -148,11 +159,8 @@
return self._unis.get(port_no_or_name)
@property
- def pon_ports(self):
- return self._pons.values()
-
- def pon_port(self, port_no):
- return self._pons.get(port_no)
+ def pon_port(self):
+ return self._pon
@property
def _next_port_number(self):
@@ -163,17 +171,10 @@
def start(self):
assert self._enabled, 'Start should only be called if enabled'
- #
- # TODO: Perform common startup tasks here
- #
+
self._cancel_deferred()
- self._omci = OMCI_CC(self.adapter_agent,
- self.device_id,
- custom_me_entries=onu_custom_entity_classes)
- self._omci.enabled = True
-
- # Handle received ONU event messages
+ # Handle received ONU event messages TODO: Deprecate this....
self._event_messages = DeferredQueue()
self._event_deferred = reactor.callLater(0, self._handle_onu_events)
@@ -181,10 +182,10 @@
self.adapter_agent.register_for_inter_adapter_messages()
# Port startup
- for port in self.uni_ports:
- port.enabled = True
+ if self._pon is not None:
+ self._pon.enabled = True
- for port in self.pon_ports:
+ for port in self.uni_ports:
port.enabled = True
# Heartbeat
@@ -203,16 +204,16 @@
# Heartbeat
self._heartbeat.stop()
+ # OMCI Communications
+ # if self._onu_omci_device is not None:
+ # self._onu_omci_device.stop()
+
# Port shutdown
for port in self.uni_ports:
port.enabled = False
- for port in self.pon_ports:
- port.enabled = False
-
- omci, self._omci = self._omci, None
- if omci is not None:
- omci.enabled = False
+ if self._pon is not None:
+ self._pon.enabled = False
queue, self._event_deferred = self._event_deferred, None
if queue is not None:
@@ -220,8 +221,8 @@
_ = yield queue.get()
def receive_message(self, msg):
- if self._omci is not None and self.enabled:
- self._omci.receive_message(msg)
+ if self.omci is not None and self.enabled:
+ self.omci.receive_message(msg)
def activate(self, device):
self.log.info('activating')
@@ -271,28 +272,26 @@
# Need to query ONU for number of supported uni ports
# For now, temporarily set number of ports to 1 - port #2
- # Register physical ports. Should have at least one of each
-
- pon_port = PonPort.create(self, self._next_port_number)
-
- self._pons[pon_port.port_number] = pon_port
- self.adapter_agent.add_port(device.id, pon_port.get_port())
-
parent_device = self.adapter_agent.get_device(device.parent_id)
self.logical_device_id = parent_device.parent_id
assert self.logical_device_id, 'Invalid logical device ID'
+ # 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
- uni_port = UniPort.create(self, self._next_port_number,
- 'deprecated', device.vlan)
+ 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, control_vlan=device.vlan)
+ uni_port.add_logical_port(device.vlan, subscriber_vlan=device.vlan)
# Start things up for this ONU Handler.
self.enabled = True
@@ -376,7 +375,7 @@
# self.log.info('bulk-flow-update', device_id=device.id, flows=flows)
import voltha.core.flow_decomposer as fd
- from voltha.protos.openflow_13_pb2 import OFPXMC_OPENFLOW_BASIC, ofp_port
+ from voltha.protos.openflow_13_pb2 import OFPXMC_OPENFLOW_BASIC
def is_downstream(port):
return port == 100 # Need a better way
@@ -384,7 +383,7 @@
def is_upstream(port):
return not is_downstream(port)
- omci = self._omci
+ omci = self.omci
for flow in flows:
_type = None
@@ -482,7 +481,7 @@
elif action.type == fd.PUSH_VLAN:
_push_tpid = action.push.ethertype
- log.info('action-type-push-vlan',
+ self.log.info('action-type-push-vlan',
push_tpid=_push_tpid, in_port=_in_port)
if action.push.ethertype != 0x8100:
self.log.error('unhandled-tpid',
@@ -501,9 +500,8 @@
self.log.error('unsupported-action-set-field-type',
field_type=_field.type)
else:
- log.error('unsupported-action-type',
- action_type=action.type, in_port=_in_port)
-
+ self.log.error('unsupported-action-type',
+ action_type=action.type, in_port=_in_port)
#
# All flows created from ONU adapter should be OMCI based
#
@@ -541,7 +539,7 @@
'''
except Exception as e:
- log.exception('failed-to-install-flow', e=e, flow=flow)
+ self.log.exception('failed-to-install-flow', e=e, flow=flow)
@inlineCallbacks
def reboot(self):
@@ -627,7 +625,6 @@
self.log.info('reboot-complete', device_id=self.device_id)
-
def self_test_device(self, device):
"""
This is called to Self a device based on a NBI call.
@@ -673,9 +670,9 @@
portid=port_id)
# Remove pon port from parent
- for port in self.pon_ports:
+ if self._pon is not None:
self.adapter_agent.delete_port_reference_from_parent(self.device_id,
- port.get_port())
+ self._pon.get_port())
# Just updating the port status may be an option as well
# port.ofp_port.config = OFPPC_NO_RECV
@@ -715,10 +712,10 @@
# self.uni_port = self._get_uni_port() deprecated
# Add the pon port reference to the parent
- for port in self.pon_ports:
+ if self._pon is not None:
# TODO: Send 'enable' to PonPort?
self.adapter_agent.add_port_reference_to_parent(device.id,
- port.get_port())
+ self._pon.get_port())
# Update the connect status to REACHABLE
device.connect_status = ConnectStatus.REACHABLE
@@ -732,7 +729,7 @@
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,
- control_vlan=device.vlan)
+ subscriber_vlan=device.vlan)
device = self.adapter_agent.get_device(device.id)
device.oper_status = OperStatus.ACTIVE
@@ -747,6 +744,12 @@
def delete(self):
self.log.info('deleting', device_id=self.device_id)
+
+ # OpenOMCI cleanup
+ if self._omci_agent is not None:
+ self._omci_agent.remove_device(self.device_id, cleanup=True)
+ #self._onu_omci_device = None
+ self._omci_agent = None
#
# handling needed here
# self.enabled = False
@@ -783,7 +786,7 @@
def _check_for_mock_config(self, data):
# Check for MOCK configuration
description = data.get('description')
- if description is not None and description.lower() == 'mock':
+ if description is not None and 'mock' in description.lower():
self._is_mock = True
def on_ont_ani_create(self, ont_ani):
@@ -919,16 +922,22 @@
# Set a random serial number since not xPON based
device = self.adapter_agent.get_device(self.device_id)
- ofp_port_no, cntl_vlan = UniPort.decode_openflow_port_and_control_vlan(self, venet)
+ ofp_port_no, subscriber_vlan, untagged_vlan = UniPort.decode_venet(venet)
uni_port = UniPort.create(self, venet['name'],
self._next_port_number,
- cntl_vlan)
+ 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())
- uni_port.add_logical_port(ofp_port_no, control_vlan=cntl_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._pon.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
@@ -987,11 +996,8 @@
tcont['object'] = OnuTCont.create(self, tcont, traffic_descriptor,
is_mock=self.is_mock)
- # Look up any PON port # TODO: Add the vont-ani 'name' to the PON Port and look up that way
- pon_port = self.pon_ports[0]
-
- if pon_port is not None:
- pon_port.add_tcont(tcont['object'])
+ if self._pon is not None:
+ self._pon.add_tcont(tcont['object'])
return tcont
@@ -1007,25 +1013,20 @@
update['object'] = tc
- # Look up any PON port # TODO: Add the vont-ani 'name' to the PON Port and look up that way
- pon_port = self.pon_ports[0]
-
- if pon_port is not None:
+ if self._pon is not None:
keys = [k for k in diffs.keys() if k in valid_keys]
for k in keys:
if k == 'td-ref':
td = self.traffic_descriptors.get(update['td-ref'])
if td is not None:
- pon_port.update_tcont_td(tcont['alloc-id'], td)
+ self._pon.update_tcont_td(tcont['alloc-id'], td)
return update
def on_tcont_delete(self, tcont):
- pon_port = self.pon_ports[0] # Look up any PON port # TODO: Add the vont-ani 'name' to the PON Port and look up that way
-
- if pon_port is not None:
- pon_port.remove_tcont(tcont['alloc-id'])
+ if self._pon is not None:
+ self._pon.remove_tcont(tcont['alloc-id'])
return None
@@ -1056,10 +1057,8 @@
if val['td-ref'] == td_name and td_name is not None}
for tcont in tconts.itervalues():
- pon_port = self.pon_ports[0] # Look up any PON port # TODO: Add the vont-ani 'name' to the PON Port and look up that way
-
- if pon_port is not None:
- pon_port.update_tcont_td(tcont['alloc-id'], update['object'])
+ if self._pon is not None:
+ self._pon.update_tcont_td(tcont['alloc-id'], update['object'])
return update
@@ -1074,13 +1073,12 @@
def on_gemport_create(self, gem_port):
from onu_gem_port import OnuGemPort
+ assert self._pon is not None, 'No PON port'
- gem_port['object'] = OnuGemPort.create(self, gem_port, is_mock=self.is_mock)
- # Look up any PON port # TODO: Add the vont-ani 'name' to the PON Port and look up that way
- pon_port = self.pon_ports[0]
- if pon_port is not None:
- pon_port.add_gem_port(gem_port['object'])
-
+ gem_port['object'] = OnuGemPort.create(self, gem_port,
+ self._pon.next_gem_entity_id,
+ is_mock=self.is_mock)
+ self._pon.add_gem_port(gem_port['object'])
return gem_port
def on_gemport_modify(self, gem_port, update, diffs):
@@ -1106,9 +1104,8 @@
return update
def on_gemport_delete(self, gem_port):
- pon_port = self.pon_ports[0] # Look up any PON port # TODO: Add the vont-ani 'name' to the PON Port and look up that way
- if pon_port is not None:
- pon_port.remove_gem_id(gem_port['gemport-id'])
+ if self._pon is not None:
+ self._pon.remove_gem_id(gem_port['gemport-id'])
return None
diff --git a/voltha/adapters/adtran_onu/heartbeat.py b/voltha/adapters/adtran_onu/heartbeat.py
index 4e54459..b35daab 100644
--- a/voltha/adapters/adtran_onu/heartbeat.py
+++ b/voltha/adapters/adtran_onu/heartbeat.py
@@ -15,7 +15,8 @@
import structlog
from twisted.internet import reactor
from voltha.protos.common_pb2 import OperStatus, ConnectStatus
-from omci.omci_me import OntGFrame
+from voltha.extensions.omci.omci_me import OntGFrame
+
class HeartBeat(object):
"""Wraps health-check support for ONU"""
diff --git a/voltha/adapters/adtran_onu/omci/deprecated.py b/voltha/adapters/adtran_onu/omci/deprecated.py
deleted file mode 100644
index f93df0b..0000000
--- a/voltha/adapters/adtran_onu/omci/deprecated.py
+++ /dev/null
@@ -1,498 +0,0 @@
-# Copyright 2017-present Adtran, Inc.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-from voltha.extensions.omci.omci import *
-
-
-DEFAULT_OMCI_TIMEOUT = 3 # Seconds
-
-# TODO: These are the older-style OMCI commands to send get/create/... frames
-
-
-def send_get_OntG(omci_cc, attribute, entity_id=0, timeout=DEFAULT_OMCI_TIMEOUT):
- omci_cc.log.debug('send_get_OntG')
- frame = OmciFrame(
- transaction_id=omci_cc._get_tx_tid(),
- message_type=OmciGet.message_id,
- omci_message=OmciGet(
- entity_class=OntG.class_id,
- entity_id=entity_id,
- attributes_mask=OntG.mask_for(attribute)
- )
- )
- return omci_cc.send(frame, timeout)
-
-
-def send_create_vlan_tagging_filter_data(omci_cc, entity_id, vlan_id,
- timeout=DEFAULT_OMCI_TIMEOUT):
- frame = OmciFrame(
- transaction_id=omci_cc._get_tx_tid(),
- message_type=OmciCreate.message_id,
- omci_message=OmciCreate(
- entity_class=VlanTaggingFilterData.class_id,
- entity_id=entity_id,
- data=dict(
- vlan_filter_0=vlan_id,
- forward_operation=0x10,
- number_of_entries=1
- )
- )
- )
- return omci_cc.send(frame, timeout)
-
-# TODO: Deprecated: replaced with send_set_pptp_ethernet_uni (need to clean up)
-
-
-def send_set_adminState(omci_cc, entity_id, timeout=DEFAULT_OMCI_TIMEOUT):
- omci_cc.log.debug('send_set_AdminState')
- data = dict(
- administrative_state=0
- )
- frame = OmciFrame(
- transaction_id=omci_cc._get_tx_tid(),
- message_type=OmciSet.message_id,
- omci_message=OmciSet(
- entity_class=PptpEthernetUni.class_id,
- entity_id=entity_id,
- attributes_mask=PptpEthernetUni.mask_for(*data.keys()),
- data=data
- )
- )
- return omci_cc.send(frame, timeout)
-
-
-def send_get_SoftwareImage(omci_cc, attribute, entity_id=0, timeout=DEFAULT_OMCI_TIMEOUT):
- omci_cc.log.debug('send_get_SoftwareImage')
- frame = OmciFrame(
- transaction_id=omci_cc._get_tx_tid(),
- message_type=OmciGet.message_id,
- omci_message=OmciGet(
- entity_class=SoftwareImage.class_id,
- entity_id=entity_id,
- attributes_mask=SoftwareImage.mask_for(attribute)
- )
- )
- return omci_cc.send(frame, timeout)
-
-
-def send_set_extended_vlan_tagging_operation_vlan_configuration_data_untagged(omci_cc,
- entity_id,
- filter_inner_vid,
- treatment_inner_vid,
- timeout=DEFAULT_OMCI_TIMEOUT):
- data = 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=filter_inner_vid,
- 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=treatment_inner_vid,
- treatment_inner_tpid_de=4
- )
- )
- frame = OmciFrame(
- transaction_id=omci_cc._get_tx_tid(),
- message_type=OmciSet.message_id,
- omci_message=OmciSet(
- entity_class=
- ExtendedVlanTaggingOperationConfigurationData.class_id,
- entity_id=entity_id,
- attributes_mask=
- ExtendedVlanTaggingOperationConfigurationData.mask_for(
- *data.keys()),
- data=data
- )
- )
- return omci_cc.send(frame, timeout)
-
-
-def send_set_extended_vlan_tagging_operation_vlan_configuration_data_single_tag(omci_cc,
- entity_id,
- filter_inner_priority,
- filter_inner_vid,
- filter_inner_tpid_de,
- treatment_tags_to_remove,
- treatment_inner_priority,
- treatment_inner_vid,
- timeout=DEFAULT_OMCI_TIMEOUT):
- data = dict(
- received_frame_vlan_tagging_operation_table=
- VlanTaggingOperation(
- filter_outer_priority=15,
- filter_outer_vid=4096,
- filter_outer_tpid_de=0,
- filter_inner_priority=filter_inner_priority,
- filter_inner_vid=filter_inner_vid,
- filter_inner_tpid_de=filter_inner_tpid_de,
- filter_ether_type=0,
- treatment_tags_to_remove=treatment_tags_to_remove,
- treatment_outer_priority=15,
- treatment_outer_vid=0,
- treatment_outer_tpid_de=0,
- treatment_inner_priority=treatment_inner_priority,
- treatment_inner_vid=treatment_inner_vid,
- treatment_inner_tpid_de=4
- )
- )
- frame = OmciFrame(
- transaction_id=omci_cc._get_tx_tid(),
- message_type=OmciSet.message_id,
- omci_message=OmciSet(
- entity_class=
- ExtendedVlanTaggingOperationConfigurationData.class_id,
- entity_id=entity_id,
- attributes_mask=
- ExtendedVlanTaggingOperationConfigurationData.mask_for(
- *data.keys()),
- data=data
- )
- )
- return omci_cc.send(frame, timeout)
-
-
-def send_delete_vlan_tagging_filter_data(omci_cc,
- entity_id):
- frame = OmciFrame(
- transaction_id=omci_cc._get_tx_tid(),
- message_type=OmciDelete.message_id,
- omci_message=OmciDelete(
- entity_class=VlanTaggingFilterData.class_id,
- entity_id=entity_id
- )
- )
- return omci_cc.send(frame)
-
-# xxxxxxxxxxxxxxxxxxxxxxxxxxxxx
-
-
-def send_set_tcont(omci_cc, entity_id, alloc_id, timeout=DEFAULT_OMCI_TIMEOUT):
- data = dict(
- alloc_id=alloc_id
- )
- frame = OmciFrame(
- transaction_id=omci_cc._get_tx_tid(),
- message_type=OmciSet.message_id,
- omci_message=OmciSet(
- entity_class=Tcont.class_id,
- entity_id=entity_id,
- attributes_mask=Tcont.mask_for(*data.keys()),
- data=data
- )
- )
- return omci_cc.send(frame, timeout)
-
-
-def send_create_gem_port_network_ctp(omci_cc, entity_id, port_id,
- tcont_id, direction, tm,
- timeout=DEFAULT_OMCI_TIMEOUT):
-
- _directions = {"upstream": 1, "downstream": 2, "bi-directional": 3}
-
- if _directions.has_key(direction):
- _direction = _directions[direction]
- else:
- omci_cc.log.error('invalid-gem-port-direction', direction=direction)
- raise ValueError('Invalid GEM port direction: {_dir}'.format(_dir=direction))
-
- frame = OmciFrame(
- transaction_id=omci_cc._get_tx_tid(),
- message_type=OmciCreate.message_id,
- omci_message=OmciCreate(
- entity_class=GemPortNetworkCtp.class_id,
- entity_id=entity_id,
- data=dict(
- port_id=port_id,
- tcont_pointer=tcont_id,
- direction=_direction,
- traffic_management_pointer_upstream=tm
- )
- )
- )
- return omci_cc.send(frame, timeout)
-
-
-def send_set_8021p_mapper_service_profile(omci_cc, entity_id,
- interwork_tp_id,
- timeout=DEFAULT_OMCI_TIMEOUT):
- data = dict(
- interwork_tp_pointer_for_p_bit_priority_0=interwork_tp_id,
- interwork_tp_pointer_for_p_bit_priority_1=interwork_tp_id,
- interwork_tp_pointer_for_p_bit_priority_2=interwork_tp_id,
- interwork_tp_pointer_for_p_bit_priority_3=interwork_tp_id,
- interwork_tp_pointer_for_p_bit_priority_4=interwork_tp_id,
- interwork_tp_pointer_for_p_bit_priority_5=interwork_tp_id,
- interwork_tp_pointer_for_p_bit_priority_6=interwork_tp_id,
- interwork_tp_pointer_for_p_bit_priority_7=interwork_tp_id
- )
- frame = OmciFrame(
- transaction_id=omci_cc._get_tx_tid(),
- message_type=OmciSet.message_id,
- omci_message=OmciSet(
- entity_class=Ieee8021pMapperServiceProfile.class_id,
- entity_id=entity_id,
- attributes_mask=Ieee8021pMapperServiceProfile.mask_for(
- *data.keys()),
- data=data
- )
- )
- return omci_cc.send(frame, timeout)
-
-
-def send_create_8021p_mapper_service_profile(omci_cc, entity_id, timeout=DEFAULT_OMCI_TIMEOUT):
- frame = OmciFrame(
- transaction_id=omci_cc._get_tx_tid(),
- message_type=OmciCreate.message_id,
- omci_message=OmciCreate(
- entity_class=Ieee8021pMapperServiceProfile.class_id,
- entity_id=entity_id,
- data=dict(
- tp_pointer=OmciNullPointer,
- interwork_tp_pointer_for_p_bit_priority_0=OmciNullPointer,
- interwork_tp_pointer_for_p_bit_priority_1=OmciNullPointer,
- interwork_tp_pointer_for_p_bit_priority_2=OmciNullPointer,
- interwork_tp_pointer_for_p_bit_priority_3=OmciNullPointer,
- interwork_tp_pointer_for_p_bit_priority_4=OmciNullPointer,
- interwork_tp_pointer_for_p_bit_priority_5=OmciNullPointer,
- interwork_tp_pointer_for_p_bit_priority_6=OmciNullPointer,
- interwork_tp_pointer_for_p_bit_priority_7=OmciNullPointer
- )
- )
- )
- return omci_cc.send(frame, timeout)
-
-
-def send_create_mac_bridge_service_profile(omci_cc, entity_id, timeout=DEFAULT_OMCI_TIMEOUT):
- frame = OmciFrame(
- transaction_id=omci_cc._get_tx_tid(),
- message_type=OmciCreate.message_id,
- omci_message=OmciCreate(
- entity_class=MacBridgeServiceProfile.class_id,
- entity_id=entity_id,
- data=dict(
- spanning_tree_ind=False,
- # BP: Hack , this was not set in ADT configuration
- # learning_ind=True,
- # priority=0x8000,
- # max_age=20 * 256,
- # hello_time=2 * 256,
- # forward_delay=15 * 256,
- # unknown_mac_address_discard=True
- )
- )
- )
- return omci_cc.send(frame, timeout)
-
-
-def send_create_gal_ethernet_profile(omci_cc, entity_id, max_gem_payload_size,
- timeout=DEFAULT_OMCI_TIMEOUT):
- frame = OmciFrame(
- transaction_id=omci_cc._get_tx_tid(),
- message_type=OmciCreate.message_id,
- omci_message=OmciCreate(
- entity_class=GalEthernetProfile.class_id,
- entity_id=entity_id,
- data=dict(
- max_gem_payload_size=max_gem_payload_size
- )
- )
- )
- return omci_cc.send(frame, timeout)
-
-
-def send_create_gem_inteworking_tp(omci_cc, entity_id, gem_port_net_ctp_id,
- service_profile_id, timeout=DEFAULT_OMCI_TIMEOUT):
- frame = OmciFrame(
- transaction_id=omci_cc._get_tx_tid(),
- message_type=OmciCreate.message_id,
- omci_message=OmciCreate(
- entity_class=GemInterworkingTp.class_id,
- entity_id=entity_id,
- data=dict(
- gem_port_network_ctp_pointer=gem_port_net_ctp_id,
- interworking_option=5,
- service_profile_pointer=service_profile_id,
- interworking_tp_pointer=0x0,
- pptp_counter=1,
- gal_profile_pointer=0x0 # BP: HACK old value 0x1
- )
- )
- )
- return omci_cc.send(frame, timeout)
-
-
-def send_create_mac_bridge_port_configuration_data(omci_cc, entity_id, bridge_id,
- port_id, tp_type, tp_id,
- timeout=DEFAULT_OMCI_TIMEOUT):
- frame = OmciFrame(
- transaction_id=omci_cc._get_tx_tid(),
- message_type=OmciCreate.message_id,
- omci_message=OmciCreate(
- entity_class=MacBridgePortConfigurationData.class_id,
- entity_id=entity_id,
- data=dict(
- bridge_id_pointer=bridge_id,
- port_num=port_id,
- tp_type=tp_type,
- tp_pointer=tp_id
- )
- )
- )
- return omci_cc.send(frame, timeout)
-
-
-def send_get_circuit_pack(omci_cc, attribute, entity_id=0,
- timeout=DEFAULT_OMCI_TIMEOUT):
- omci_cc.log.debug('send_get_circuit_pack')
- frame = OmciFrame(
- transaction_id=omci_cc._get_tx_tid(),
- message_type=OmciGet.message_id,
- omci_message=OmciGet(
- entity_class=CircuitPack.class_id,
- entity_id=entity_id,
- attributes_mask=CircuitPack.mask_for(attribute)
- )
- )
- return omci_cc.send(frame, timeout)
-
-
-def send_get_device_info(omci_cc, attribute, entity_id=0, timeout=DEFAULT_OMCI_TIMEOUT):
- frame = OmciFrame(
- transaction_id=omci_cc._get_tx_tid(),
- message_type=OmciGet.message_id,
- omci_message=OmciGet(
- entity_class=CircuitPack.class_id,
- entity_id=entity_id,
- attributes_mask=CircuitPack.mask_for(attribute)
- )
- )
- return omci_cc.send(frame, timeout)
-
-
-def send_get_Ont2G(omci_cc, attribute, entity_id=0, timeout=DEFAULT_OMCI_TIMEOUT):
- omci_cc.log.debug('send_get_Ont2G')
- frame = OmciFrame(
- transaction_id=omci_cc._get_tx_tid(),
- message_type=OmciGet.message_id,
- omci_message=OmciGet(
- entity_class=Ont2G.class_id,
- entity_id=entity_id,
- attributes_mask=Ont2G.mask_for(attribute)
- )
- )
- return omci_cc.send(frame, timeout)
-
-
-def send_get_cardHolder(omci_cc, attribute, entity_id=0, timeout=DEFAULT_OMCI_TIMEOUT):
- omci_cc.log.debug('send_get_cardHolder')
- frame = OmciFrame(
- transaction_id=omci_cc._get_tx_tid(),
- message_type=OmciGet.message_id,
- omci_message=OmciGet(
- entity_class=Cardholder.class_id,
- entity_id=entity_id,
- attributes_mask=Cardholder.mask_for(attribute)
- )
- )
- return omci_cc.send(frame, timeout)
-
-
-def send_set_pptp_ethernet_uni(omci_cc, entity_id, timeout=DEFAULT_OMCI_TIMEOUT):
- omci_cc.log.debug('send_set_AdminState')
- data = dict(
- administrative_state=0
- )
- frame = OmciFrame(
- transaction_id=omci_cc._get_tx_tid(),
- message_type=OmciSet.message_id,
- omci_message=OmciSet(
- entity_class=PptpEthernetUni.class_id,
- entity_id=entity_id,
- attributes_mask=PptpEthernetUni.mask_for(*data.keys()),
- data=data
- )
- )
- return omci_cc.send(frame, timeout)
-
-
-def send_get_IpHostConfigData(omci_cc, attribute, entity_id=0, timeout=DEFAULT_OMCI_TIMEOUT):
- omci_cc.log.debug('send_get_IpHostConfigData')
- frame = OmciFrame(
- transaction_id=omci_cc._get_tx_tid(),
- message_type=OmciGet.message_id,
- omci_message=OmciGet(
- entity_class=IpHostConfigData.class_id,
- entity_id=entity_id,
- attributes_mask=IpHostConfigData.mask_for(attribute)
- )
- )
- return omci_cc.send(frame, timeout)
-
-
-def send_create_extended_vlan_tagging_operation_configuration_data(omci_cc,
- entity_id,
- assoc_type,
- assoc_me,
- timeout=DEFAULT_OMCI_TIMEOUT):
- frame = OmciFrame(
- transaction_id=omci_cc._get_tx_tid(),
- message_type=OmciCreate.message_id,
- omci_message=OmciCreate(
- entity_class=
- ExtendedVlanTaggingOperationConfigurationData.class_id,
- entity_id=entity_id,
- data=dict(
- association_type=assoc_type,
- associated_me_pointer=assoc_me
- )
- )
- )
- return omci_cc.send(frame, timeout)
-
-
-def send_set_extended_vlan_tagging_operation_tpid_configuration_data(omci_cc,
- entity_id,
- input_tpid,
- output_tpid,
- timeout=DEFAULT_OMCI_TIMEOUT):
- data = dict(
- input_tpid=input_tpid,
- output_tpid=output_tpid,
- downstream_mode=0, # inverse of upstream
- )
- frame = OmciFrame(
- transaction_id=omci_cc._get_tx_tid(),
- message_type=OmciSet.message_id,
- omci_message=OmciSet(
- entity_class=
- ExtendedVlanTaggingOperationConfigurationData.class_id,
- entity_id=entity_id,
- attributes_mask=
- ExtendedVlanTaggingOperationConfigurationData.mask_for(
- *data.keys()),
- data=data
- )
- )
- return omci_cc.send(frame, timeout)
diff --git a/voltha/adapters/adtran_onu/omci/me_frame.py b/voltha/adapters/adtran_onu/omci/me_frame.py
deleted file mode 100644
index 1808d04..0000000
--- a/voltha/adapters/adtran_onu/omci/me_frame.py
+++ /dev/null
@@ -1,291 +0,0 @@
-#
-# Copyright 2017 the original author or authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-"""
-OMCI Managed Entity Message support base class
-"""
-from voltha.extensions.omci.omci import *
-
-# abbreviations
-OP = EntityOperations
-AA = AttributeAccess
-
-
-class MEFrame(object):
- """Base class to help simplify Frame Creation"""
- def __init__(self, entity_class, entity_id, data):
- assert issubclass(entity_class, EntityClass), \
- "'{}' must be a subclass of MEFrame".format(entity_class)
- self.check_type(entity_id, int)
-
- if not 0 <= entity_id <= 0xFFFF:
- raise ValueError('entity_id should be 0..65535')
-
- self._class = entity_class
- self._entity_id = entity_id
- self.data = data
-
- def __str__(self):
- return '{}: Entity_ID: {}, Data: {}'.\
- format(self.entity_class_name, self._entity_id, self.data)
-
- @property
- def entity_class(self):
- """
- The Entity Class for this ME
- :return: (EntityClass) Entity class
- """
- return self._class
-
- @property
- def entity_class_name(self):
- return self._class.__name__
-
- @property
- def entity_id(self):
- """
- The Entity ID for this ME frame
- :return: (int) Entity ID (0..0xFFFF)
- """
- return self._entity_id
-
- @staticmethod
- def check_type(param, types):
- if not isinstance(param, types):
- raise TypeError("Parameter '{}' should be a {}".format(param, types))
-
- def _check_operation(self, operation):
- allowed = self.entity_class.mandatory_operations | self.entity_class.optional_operations
- assert operation in allowed, "{} not allowed for '{}'".format(operation.name,
- self.entity_class_name)
-
- def _check_attributes(self, attributes, access):
- keys = attributes.keys() if isinstance(attributes, dict) else attributes
- for attr_name in keys:
- # Bad attribute name (invalid or spelling error)?
- index = self.entity_class.attribute_name_to_index_map.get(attr_name)
- if index is None:
- raise KeyError("Attribute '{}' is not valid for '{}'".
- format(attr_name, self.entity_class_name))
- # Invalid access?
- assert access in self.entity_class.attributes[index].access, \
- "Access '{}' for attribute '{}' is not valid for '{}'".format(access.name,
- attr_name,
- self.entity_class_name)
-
- if access.value in [AA.W.value, AA.SBC.value] and isinstance(attributes, dict):
- for attr_name, value in attributes.iteritems():
- index = self.entity_class.attribute_name_to_index_map.get(attr_name)
- attribute = self.entity_class.attributes[index]
- if not attribute.valid(value):
- raise ValueError("Invalid value '{}' for attribute '{}' of '{}".
- format(value, attr_name, self.entity_class_name))
-
- @staticmethod
- def _attr_to_data(attributes):
- """
- Convert an object into the 'data' set or dictionary for get/set/create/delete
- requests.
-
- This method takes a 'string', 'list', or 'set' for get requests and
- converts it to a 'set' of attributes.
-
- For create/set requests a dictionary of attribute/value pairs is required
-
- :param attributes: (basestring, list, set, dict) attributes. For gets
- a string, list, set, or dict can be provided. For create/set
- operations, a dictionary should be provided. For delete
- the attributes may be None since they are ignored.
-
- :return: (set, dict) set for get/deletes, dict for create/set
- """
- if isinstance(attributes, basestring):
- # data = [str(attributes)]
- data = set()
- data.add(str(attributes))
-
- elif isinstance(attributes, list):
- assert all(isinstance(attr, basestring) for attr in attributes),\
- 'attribute list must be strings'
- data = {str(attr) for attr in attributes}
- assert len(data) == len(attributes), 'Attributes were not unique'
-
- elif isinstance(attributes, set):
- assert all(isinstance(attr, basestring) for attr in attributes),\
- 'attribute set must be strings'
- data = {str(attr) for attr in attributes}
-
- elif isinstance(attributes, (dict, type(None))):
- data = attributes
-
- else:
- raise TypeError("Unsupported attributes type '{}'".format(type(attributes)))
-
- return data
-
- def create(self):
- """
- Create a Create request frame for this ME
- :return: (OmciFrame) OMCI Frame
- """
- assert hasattr(self.entity_class, 'class_id'), 'class_id required for Create actions'
- assert hasattr(self, 'entity_id'), 'entity_id required for Create actions'
- assert hasattr(self, 'data'), 'data required for Create actions'
-
- data = getattr(self, 'data')
- MEFrame.check_type(data, dict)
- assert len(data) > 0, 'No attributes supplied'
-
- self._check_operation(OP.Create)
- self._check_attributes(data, AA.Writable)
-
- return OmciFrame(
- transaction_id=None,
- message_type=OmciCreate.message_id,
- omci_message=OmciCreate(
- entity_class=getattr(self.entity_class, 'class_id'),
- entity_id=getattr(self, 'entity_id'),
- data=data
- ))
-
- def delete(self):
- """
- Create a Delete request frame for this ME
- :return: (OmciFrame) OMCI Frame
- """
- self._check_operation(OP.Delete)
-
- return OmciFrame(
- transaction_id=None,
- message_type=OmciGet.message_id,
- omci_message=OmciGet(
- entity_class=getattr(self.entity_class, 'class_id'),
- entity_id=getattr(self, 'entity_id')
- ))
-
- def set(self):
- """
- Create a Set request frame for this ME
- :return: (OmciFrame) OMCI Frame
- """
- assert hasattr(self, 'data'), 'data required for Set actions'
- data = getattr(self, 'data')
- MEFrame.check_type(data, dict)
- assert len(data) > 0, 'No attributes supplied'
-
- self._check_operation(OP.Set)
- self._check_attributes(data, AA.Writable)
-
- return OmciFrame(
- transaction_id=None,
- message_type=OmciSet.message_id,
- omci_message=OmciSet(
- entity_class=getattr(self.entity_class, 'class_id'),
- entity_id=getattr(self, 'entity_id'),
- attributes_mask=self.entity_class.mask_for(*data.keys()),
- data=data
- ))
-
- def get(self):
- """
- Create a Get request frame for this ME
- :return: (OmciFrame) OMCI Frame
- """
- assert hasattr(self, 'data'), 'data required for Get actions'
- data = getattr(self, 'data')
- MEFrame.check_type(data, (list, set, dict))
- assert len(data) > 0, 'No attributes supplied'
-
- mask_set = data.keys() if isinstance(data, dict) else data
-
- self._check_operation(OP.Get)
- self._check_attributes(mask_set, AA.Readable)
-
- return OmciFrame(
- transaction_id=None,
- message_type=OmciGet.message_id,
- omci_message=OmciGet(
- entity_class=getattr(self.entity_class, 'class_id'),
- entity_id=getattr(self, 'entity_id'),
- attributes_mask=self.entity_class.mask_for(*mask_set)
- ))
-
- def reboot(self):
- """
- Create a Reboot request from for this ME
- :return: (OmciFrame) OMCI Frame
- """
- self._check_operation(OP.Reboot)
-
- return OmciFrame(
- transaction_id=None,
- message_type=OmciReboot.message_id,
- omci_message=OmciReboot(
- entity_class=getattr(self.entity_class, 'class_id'),
- entity_id=getattr(self, 'entity_id')
- ))
-
- def mib_reset(self):
- """
- Create a MIB Reset request from for this ME
- :return: (OmciFrame) OMCI Frame
- """
- self._check_operation(OP.MibReset)
-
- return OmciFrame(
- transaction_id=None,
- message_type=OmciMibReset.message_id,
- omci_message=OmciMibReset(
- entity_class=getattr(self.entity_class, 'class_id'),
- entity_id=getattr(self, 'entity_id')
- ))
-
- def mib_upload(self):
- """
- Create a MIB Upload request from for this ME
- :return: (OmciFrame) OMCI Frame
- """
- self._check_operation(OP.MibUpload)
-
- return OmciFrame(
- transaction_id=None,
- message_type=OmciMibUpload.message_id,
- omci_message=OmciMibUpload(
- entity_class=getattr(self.entity_class, 'class_id'),
- entity_id=getattr(self, 'entity_id')
- ))
-
- def mib_upload_next(self):
- """
- Create a MIB Upload Next request from for this ME
- :return: (OmciFrame) OMCI Frame
- """
- assert hasattr(self, 'data'), 'data required for Set actions'
- data = getattr(self, 'data')
- MEFrame.check_type(data, dict)
- assert len(data) > 0, 'No attributes supplied'
- assert 'mib_data_sync' in data, "'mib_data_sync' not in attributes list"
-
- self._check_operation(OP.MibUploadNext)
- self._check_attributes(data, AA.Writable)
-
- return OmciFrame(
- transaction_id=None,
- message_type=OmciMibUploadNext.message_id,
- omci_message=OmciMibUploadNext(
- entity_class=getattr(self.entity_class, 'class_id'),
- entity_id=getattr(self, 'entity_id'),
- command_sequence_number=data['mib_data_sync']
- ))
diff --git a/voltha/adapters/adtran_onu/omci/omci_cc.py b/voltha/adapters/adtran_onu/omci/omci_cc.py
deleted file mode 100644
index 413a055..0000000
--- a/voltha/adapters/adtran_onu/omci/omci_cc.py
+++ /dev/null
@@ -1,504 +0,0 @@
-#
-# Copyright 2017 the original author or authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-"""
-OMCI Message support
-"""
-
-import sys
-import arrow
-from twisted.internet import reactor, defer
-from twisted.internet.defer import DeferredQueue, TimeoutError, CancelledError, failure, fail
-from voltha.protos import third_party
-from common.frameio.frameio import hexify
-from voltha.extensions.omci.omci import *
-from omci_entities import add_onu_me_entities
-from omci_me import OntGFrame, OntDataFrame
-
-_ = third_party
-
-_MAX_INCOMING_ALARM_MESSAGES = 256
-_MAX_INCOMING_AVC_MESSAGES = 256
-_MAX_INCOMING_TEST_RESULT_MESSAGES = 64
-
-DEFAULT_OMCI_TIMEOUT = 3 # Seconds
-MAX_OMCI_REQUEST_AGE = 60 # Seconds
-MAX_OMCI_TX_ID = 0xFFFF # 2 Octets max
-
-# abbreviations
-# ECA = EntityClassAttribute
-# AA = AttributeAccess
-OP = EntityOperations
-
-
-class OMCI_CC(object):
- """ Handle OMCI Communication Channel specifics for Adtran ONUs"""
-
- def __init__(self, adapter_agent, device_id,
- custom_me_entries=None,
- alarm_queue_limit=_MAX_INCOMING_ALARM_MESSAGES,
- avc_queue_limit=_MAX_INCOMING_ALARM_MESSAGES,
- test_results_queue_limit=_MAX_INCOMING_TEST_RESULT_MESSAGES):
-
- self.log = structlog.get_logger(device_id=device_id)
- self._adapter_agent = adapter_agent
- self._device_id = device_id
- self._proxy_address = None
- self._tx_tid = 1
- self._enabled = False
- self._requests = dict() # Tx ID -> (timestamp, deferred, tx_frame, timeout)
- self._alarm_queue = DeferredQueue(size=alarm_queue_limit)
- self._avc_queue = DeferredQueue(size=avc_queue_limit)
- self._test_results_queue = DeferredQueue(size=test_results_queue_limit)
-
- # Statistics
- self._tx_frames = 0
- self._rx_frames = 0
- self._rx_unknown_tid = 0 # Rx OMCI with no Tx TID match
- self._rx_onu_frames = 0 # Autonomously generated ONU frames
- self._rx_alarm_overflow = 0 # Autonomously generated ONU alarms rx overflow
- self._rx_avc_overflow = 0 # Autonomously generated ONU AVC rx overflow
- self._rx_onu_discards = 0 # Autonomously generated ONU unknown message types
- self._rx_timeouts = 0
- self._tx_errors = 0 # Exceptions during tx request
- self._consecutive_errors = 0 # Rx & Tx errors in a row, a good RX resets this to 0
- self._reply_min = sys.maxint # Fastest successful tx -> rx
- self._reply_max = 0 # Longest successful tx -> rx
- self._reply_sum = 0.0 # Total seconds for successful tx->rx (float for average)
-
- # If a list of custom ME Entities classes were provided, insert them into
- # main class_id to entity map.
- # TODO: If this class becomes hidden from the ONU DA, move this to the OMCI State Machine runner
-
- if custom_me_entries is not None:
- add_onu_me_entities(custom_me_entries)
-
- def __str__(self):
- return "OMCISupport: {}".format(self._device_id)
-
- @property
- def enabled(self):
- return self._enabled
-
- @enabled.setter
- def enabled(self, value):
- """
- Enable/disable the OMCI Communications Channel
-
- :param value: (boolean) True to enable, False to disable
- """
- assert isinstance(value, bool), 'enabled is a boolean'
-
- if self._enabled != value:
- self._enabled = value
- if self._enabled:
- self._start()
- else:
- self._stop()
-
- @property
- def tx_frames(self):
- return self._tx_frames
-
- @property
- def rx_frames(self):
- return self._rx_frames
-
- @property
- def rx_unknown_tid(self):
- return self._rx_unknown_tid # Tx TID not found
-
- @property
- def rx_onu_frames(self):
- return self._rx_onu_frames
-
- @property
- def rx_alarm_overflow(self):
- return self._rx_alarm_overflow # Alarm ONU autonomous overflows
-
- @property
- def rx_avc_overflow(self):
- return self._rx_avc_overflow # Attribute Value change autonomous overflows
-
- @property
- def rx_onu_discards(self):
- return self._rx_onu_discards # Attribute Value change autonomous overflows
-
- @property
- def rx_timeouts(self):
- return self._rx_timeouts
-
- @property
- def tx_errors(self):
- return self._tx_errors
-
- @property
- def consecutive_errors(self):
- return self._consecutive_errors
-
- @property
- def reply_min(self):
- return int(round(self._reply_min * 1000.0)) # Milliseconds
-
- @property
- def reply_max(self):
- return int(round(self._reply_max * 1000.0)) # Milliseconds
-
- @property
- def reply_average(self):
- avg = self._reply_sum / self._rx_frames if self._rx_frames > 0 else 0.0
- return int(round(avg * 1000.0)) # Milliseconds
-
- @property
- def get_alarm_message(self):
- """
- Attempt to retrieve and remove an ONU Alarm Message from the ONU
- autonomous message queue.
-
- TODO: We may want to deprecate this, see TODO comment around line 399 in
- the _request_success() method below
-
- :return: a Deferred which fires with the next Alarm Frame available in
- the queue.
- """
- return self._alarm_queue.get()
-
- @property
- def get_avc_message(self):
- """
- Attempt to retrieve and remove an ONU Attribute Value Change (AVC)
- Message from the ONU autonomous message queue.
-
- TODO: We may want to deprecate this, see TODO comment around line 399 in
- the _request_success() method below
-
- :return: a Deferred which fires with the next AVC Frame available in
- the queue.
- """
- return self._avc_queue.get()
-
- @property
- def get_test_results(self):
- """
- Attempt to retrieve and remove an ONU Test Results Message from the
- ONU autonomous message queue.
-
- TODO: We may want to deprecate this, see TODO comment around line 399 in
- the _request_success() method below
-
- :return: a Deferred which fires with the next Test Results Frame is
- available in the queue.
- """
- return self._test_results_queue.get()
-
- def _start(self):
- """
- Start the OMCI Communications Channel
- """
- assert self._enabled, 'Start should only be called if enabled'
- #
- # TODO: Perform any other common startup tasks here
- #
- self.flush()
-
- device = self._adapter_agent.get_device(self._device_id)
- self._proxy_address = device.proxy_address
-
- def _stop(self):
- """
- Stop the OMCI Communications Channel
- """
- assert not self._enabled, 'Stop should only be called if disabled'
- #
- # TODO: Perform common shutdown tasks here
- #
- self.flush()
- self._proxy_address = None
-
- # TODO: What is best way to clean up any outstanding futures for these queues
- self._alarm_queue = None
- self._avc_queue = None
- self._test_results_queue = None
-
- def _receive_onu_message(self, rx_frame):
- """ Autonomously generated ONU frame Rx handler"""
- from twisted.internet.defer import QueueOverflow
-
- self.log.debug('rx-onu-frame', frame_type=type(rx_frame),
- frame=hexify(str(rx_frame)))
-
- # TODO: Signal, via defer if Alarm Overflow or just an event?
- msg_type = rx_frame.fields['message_type']
-
- self._rx_onu_frames += 1
-
- if msg_type == EntityOperations.AlarmNotification:
- try:
- self._alarm_queue.put((rx_frame, arrow.utcnow().float_timestamp))
-
- except QueueOverflow:
- self._rx_alarm_overflow += 1
- self.log.warn('onu-rx-alarm-overflow', cnt=self._rx_alarm_overflow)
-
- elif msg_type == EntityOperations.AttributeValueChange:
- try:
- self._alarm_queue.put((rx_frame, arrow.utcnow().float_timestamp))
-
- except QueueOverflow:
- self._rx_avc_overflow += 1
- self.log.warn('onu-rx-avc-overflow', cnt=self._rx_avc_overflow)
- else:
- # TODO: Need to add test results message support
-
- self.log.warn('onu-unsupported-autonomous-message', type=msg_type)
- self._rx_onu_discards += 1
-
- def receive_message(self, msg):
- """
- Receive and OMCI message from the proxy channel to the OLT.
-
- Call this from your ONU Adapter on a new OMCI Rx on the proxy channel
- """
- if self.enabled:
- try:
- now = arrow.utcnow()
- d = None
-
- try:
- rx_frame = OmciFrame(msg)
- rx_tid = rx_frame.fields['transaction_id']
-
- if rx_tid == 0:
- return self._receive_onu_message(rx_frame)
-
- self._rx_frames += 1
- self._consecutive_errors = 0
-
- except KeyError as e:
- # Unknown, Unsupported, or vendor-specific ME. Key is the unknown classID
- # TODO: Can we create a temporary one to hold it so upload does not always fail on new ME's?
- self.log.exception('frame-decode-key-error', msg=hexlify(msg), e=e)
- return
-
- except Exception as e:
- self.log.exception('frame-decode', msg=hexlify(msg), e=e)
- return
-
- try:
- (ts, d, _, _) = self._requests.pop(rx_tid)
-
- ts_diff = now - arrow.Arrow.utcfromtimestamp(ts)
- secs = ts_diff.total_seconds()
- self._reply_sum += secs
-
- if secs < self._reply_min:
- self._reply_min = secs
-
- if secs > self._reply_max:
- self._reply_max = secs
-
- # TODO: Could also validate response type based on request action
-
- except KeyError:
- # Possible late Rx on a message that timed-out
- self._rx_unknown_tid += 1
- self.log.warn('tx-message-missing', rx_id=rx_tid, msg=hexlify(msg))
- return
-
- except Exception as e:
- self.log.exception('frame-match', msg=hexlify(msg), e=e)
- if d is not None:
- return d.errback(failure.Failure(e))
- return
-
- d.callback(rx_frame)
-
- except Exception as e:
- self.log.exception('rx-msg', e=e)
-
- def flush(self, max_age=0):
- limit = arrow.utcnow().float_timestamp - max_age
- old = [tid for tid, (ts, _, _, _) in self._requests.iteritems()
- if ts <= limit]
-
- for tid in old:
- (_, d, _, _) = self._requests.pop(tid)
- if d is not None and not d.called:
- d.cancel()
-
- self._requests = dict()
-
- if max_age == 0:
- # Flush autonomous messages (Alarms & AVCs)
- while self._alarm_queue.pending:
- _ = yield self._alarm_queue.get()
-
- while self._avc_queue.pending:
- _ = yield self._avc_queue.get()
-
- def _get_tx_tid(self):
- """
- Get the next Transaction ID for a tx. Note TID=0 is reserved
- for autonomously generated messages from an ONU
-
- :return: (int) TID
- """
- tx_tid, self._tx_tid = self._tx_tid, self._tx_tid + 1
- if self._tx_tid > MAX_OMCI_TX_ID:
- self._tx_tid = 1
-
- return tx_tid
-
- def _request_failure(self, value, tx_tid):
- """
- Handle a transmit failure and/or Rx timeout
-
- :param value: (Failure) Twisted failure
- :param tx_tid: (int) Associated Tx TID
- """
- if tx_tid in self._requests:
- (_, _, _, timeout) = self._requests.pop(tx_tid)
- else:
- timeout = 0
-
- if isinstance(value, failure.Failure):
- value.trap(CancelledError)
- self._rx_timeouts += 1
- self._consecutive_errors += 1
- self.log.info('timeout', tx_id=tx_tid, timeout=timeout)
- value = failure.Failure(TimeoutError(timeout, "Deferred"))
-
- return value
-
- def _request_success(self, rx_frame):
- """
- Handle transmit success (a matching Rx was received)
-
- :param rx_frame: (OmciFrame) OMCI response frame with matching TID
- :return: (OmciFrame) OMCI response frame with matching TID
- """
- #
- # TODO: Here we could update the MIB database if we did a set/create/delete
- # or perhaps a verify if a GET. Also could increment mib counter
- #
- # TODO: A better way to perform this in VOLTHA v1.3 would be to provide
- # a pub/sub capability for external users/tasks to monitor responses
- # that could optionally take a filter. This would allow a MIB-Sync
- # task to easily watch all AVC notifications as well as Set/Create/Delete
- # operations and keep them serialized. It may also be a better/easier
- # way to handle things if we containerize OpenOMCI.
- #
- try:
- if isinstance(rx_frame.omci_message, OmciGetResponse):
- pass # TODO: Implement MIB check or remove
-
- elif isinstance(rx_frame.omci_message, OmciSetResponse):
- pass # TODO: Implement MIB update
-
- elif isinstance(rx_frame.omci_message, OmciCreateResponse):
- pass # TODO: Implement MIB update
-
- elif isinstance(rx_frame.omci_message, OmciDeleteResponse):
- pass # TODO: Implement MIB update
-
- except Exception as e:
- self.log.exception('omci-message', e=e)
-
- return rx_frame
-
- def send(self, frame, timeout=DEFAULT_OMCI_TIMEOUT):
- """
- Send the OMCI Frame to the ONU via the proxy_channel
-
- :param frame: (OMCIFrame) Message to send
- :param timeout: (int) Rx Timeout. 0=Forever
- :return: (deferred) A deferred that fires when the response frame is received
- or if an error/timeout occurs
- """
- self.flush(max_age=MAX_OMCI_REQUEST_AGE)
-
- assert timeout <= MAX_OMCI_REQUEST_AGE, \
- 'Maximum timeout is {} seconds'.format(MAX_OMCI_REQUEST_AGE)
- assert isinstance(frame, OmciFrame), \
- "Invalid frame class '{}'".format(type(frame))
-
- if not self.enabled or self._proxy_address is None:
- # TODO custom exceptions throughout this code would be helpful
- return fail(result=failure.Failure(Exception('OMCI is not enabled')))
-
- try:
- tx_tid = frame.fields['transaction_id']
- if tx_tid is None:
- tx_tid = self._get_tx_tid()
- frame.fields['transaction_id'] = tx_tid
-
- assert tx_tid not in self._requests, 'TX TID {} is already exists'.format(tx_tid)
- assert tx_tid >= 0, 'Invalid Tx TID: {}'.format(tx_tid)
-
- ts = arrow.utcnow().float_timestamp
- d = defer.Deferred()
-
- self._adapter_agent.send_proxied_message(self._proxy_address,
- hexify(str(frame)))
- self._tx_frames += 1
- self._requests[tx_tid] = (ts, d, frame, timeout)
-
- d.addCallbacks(self._request_success, self._request_failure,
- errbackArgs=(tx_tid,))
-
- if timeout > 0:
- d.addTimeout(timeout, reactor)
-
- except Exception as e:
- self._tx_errors += 1
- self._consecutive_errors += 1
- self.log.exception('send-omci', e=e)
- return fail(result=failure.Failure(e))
-
- return d
-
- ###################################################################################
- # MIB Action shortcuts
-
- def send_mib_reset(self, timeout=DEFAULT_OMCI_TIMEOUT):
- """
- Perform a MIB Reset
- """
- self.log.debug('send-mib-reset')
-
- frame = OntDataFrame().mib_reset()
- return self.send(frame, timeout)
-
- def send_mib_upload(self, timeout=DEFAULT_OMCI_TIMEOUT):
- self.log.debug('send-mib-upload')
-
- frame = OntDataFrame().mib_upload()
- return self.send(frame, timeout)
-
- def send_mib_upload_next(self, seq_no, timeout=DEFAULT_OMCI_TIMEOUT):
- self.log.debug('send-mib-upload-next')
-
- frame = OntDataFrame(seq_no).mib_upload_next()
- return self.send(frame, timeout)
-
- def send_reboot(self, timeout=DEFAULT_OMCI_TIMEOUT):
- """
- Send an ONU Device reboot request (ONU-G ME).
- """
- self.log.debug('send-mib-reboot')
-
- frame = OntGFrame().reboot()
- return self.send(frame, timeout)
diff --git a/voltha/adapters/adtran_onu/omci/omci_defs.py b/voltha/adapters/adtran_onu/omci/omci_defs.py
deleted file mode 100644
index 5b3f5a8..0000000
--- a/voltha/adapters/adtran_onu/omci/omci_defs.py
+++ /dev/null
@@ -1,32 +0,0 @@
-#
-# Copyright 2017 the original author or authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-""" Additional definitions not found in OMCI library"""
-
-from enum import Enum
-
-
-class ReasonCodes(Enum):
- # OMCI Result and reason codes
-
- Success = 0, # Command processed successfully
- ProcessingError = 1, # Command processing error
- NotSupported = 2, # Command not supported
- ParameterError = 3, # Parameter error
- UnknownEntity = 4, # Unknown managed entity
- UnknownInstance = 5, # Unknown managed entity instance
- DeviceBusy = 6, # Device busy
- InstanceExists = 7, # Instance Exists
- AttributeFailure = 9, # Attribute(s) failed or unknown
diff --git a/voltha/adapters/adtran_onu/omci/omci_entities.py b/voltha/adapters/adtran_onu/omci/omci_entities.py
index 62c8516..6cef5ee 100644
--- a/voltha/adapters/adtran_onu/omci/omci_entities.py
+++ b/voltha/adapters/adtran_onu/omci/omci_entities.py
@@ -17,10 +17,7 @@
import inspect
import sys
-from scapy.fields import ByteField, ShortField
-from scapy.fields import IntField, StrFixedLenField
-
-
+from scapy.fields import ShortField
from voltha.extensions.omci.omci_entities import EntityClassAttribute, \
AttributeAccess, EntityOperations, EntityClass
@@ -89,21 +86,18 @@
#################################################################################
# entity class lookup table from entity_class values
_onu_entity_classes_name_map = dict(
- inspect.getmembers(sys.modules[__name__],
- lambda o: inspect.isclass(o) and
- issubclass(o, EntityClass) and
- o is not EntityClass)
+ inspect.getmembers(sys.modules[__name__], lambda o:
+ inspect.isclass(o) and issubclass(o, EntityClass) and o is not EntityClass)
)
-
-onu_custom_entity_classes = [c for c in _onu_entity_classes_name_map.itervalues()]
+_onu_custom_entity_classes = [c for c in _onu_entity_classes_name_map.itervalues()]
+_onu_custom_entity_id_to_class_map = dict()
-def add_onu_me_entities(new_me_classes):
- from voltha.extensions.omci.omci_entities import entity_classes, entity_id_to_class_map
-
- for entity_class in new_me_classes:
- assert entity_class.class_id not in entity_id_to_class_map, \
+def onu_custom_me_entities():
+ for entity_class in _onu_custom_entity_classes:
+ assert entity_class.class_id not in _onu_custom_entity_id_to_class_map, \
"Class ID '{}' already exists in the class map".format(entity_class.class_id)
- entity_id_to_class_map[entity_class.class_id] = entity_class
+ _onu_custom_entity_id_to_class_map[entity_class.class_id] = entity_class
- entity_classes.extend(new_me_classes)
+ return _onu_custom_entity_id_to_class_map
+
diff --git a/voltha/adapters/adtran_onu/omci/omci_me.py b/voltha/adapters/adtran_onu/omci/omci_me.py
deleted file mode 100644
index 6b26ad5..0000000
--- a/voltha/adapters/adtran_onu/omci/omci_me.py
+++ /dev/null
@@ -1,664 +0,0 @@
-#
-# Copyright 2017 the original author or authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-"""
-OMCI Managed Entity Frame support
-"""
-from voltha.extensions.omci.omci import *
-from me_frame import MEFrame
-
-
-class CardholderFrame(MEFrame):
- """
- This managed entity represents fixed equipment slot configuration
- for the ONU
- """
- def __init__(self, single, slot_number, attributes):
- """
- :param single:(bool) True if the ONU is a single piece of integrated equipment,
- False if the ONU contains pluggable equipment modules
- :param slot_number: (int) slot number (0..254)
-
- :param attributes: (basestring, list, set, dict) attributes. For gets
- a string, list, or set can be provided. For create/set
- operations, a dictionary should be provided, for
- deletes None may be specified.
- """
- # Validate
- MEFrame.check_type(single, bool)
- MEFrame.check_type(slot_number, int)
- if not 0 <= slot_number <= 254:
- raise ValueError('slot_number should be 0..254')
-
- entity_id = 256 + slot_number if single else slot_number
-
- super(CardholderFrame, self).__init__(Cardholder, entity_id,
- MEFrame._attr_to_data(attributes))
-
-
-class CircuitPackFrame(MEFrame):
- """
- This managed entity models a real or virtual circuit pack that is equipped in
- a real or virtual ONU slot.
- """
- def __init__(self, entity_id, attributes):
- """
- :param entity_id: (int) This attribute uniquely identifies each instance of
- this managed entity. Its value is the same as that
- of the cardholder managed entity containing this
- circuit pack instance. (0..65535)
-
- :param attributes: (basestring, list, set, dict) attributes. For gets
- a string, list, or set can be provided. For create/set
- operations, a dictionary should be provided, for
- deletes None may be specified.
- """
- super(CircuitPackFrame, self).__init__(CircuitPack, entity_id,
- MEFrame._attr_to_data(attributes))
-
-
-class ExtendedVlanTaggingOperationConfigurationDataFrame(MEFrame):
- """
- This managed entity organizes data associated with VLAN tagging. Regardless
- of its point of attachment, the specified tagging operations refer to the
- upstream direction.
- """
- def __init__(self, entity_id, attributes):
- """
- :param entity_id: (int) This attribute uniquely identifies each instance of
- this managed entity. Its value is the same as that
- of the cardholder managed entity containing this
- circuit pack instance. (0..65535)
-
- :param attributes: (basestring, list, set, dict) attributes. For gets
- a string, list, or set can be provided. For create/set
- operations, a dictionary should be provided, for
- deletes None may be specified.
- """
- super(ExtendedVlanTaggingOperationConfigurationDataFrame,
- self).__init__(ExtendedVlanTaggingOperationConfigurationData,
- entity_id,
- MEFrame._attr_to_data(attributes))
-
-
-class IpHostConfigDataFrame(MEFrame):
- """
- The IP host config data configures IPv4 based services offered on the ONU.
- """
- def __init__(self, entity_id, attributes):
- """
- :param entity_id: (int) This attribute uniquely identifies each instance of
- this managed entity. (0..65535)
-
- :param attributes: (basestring, list, set, dict) attributes. For gets
- a string, list, or set can be provided. For create/set
- operations, a dictionary should be provided, for
- deletes None may be specified.
- """
- super(IpHostConfigDataFrame, self).__init__(IpHostConfigData,
- entity_id,
- MEFrame._attr_to_data(attributes))
-
-
-class GalEthernetProfileFrame(MEFrame):
- """
- This managed entity organizes data that describe the GTC adaptation layer
- processing functions of the ONU for Ethernet services.
- """
- def __init__(self, entity_id, max_gem_payload_size=None):
- """
- :param entity_id: (int) This attribute uniquely identifies each instance of
- this managed entity. (0..65535)
-
- :param max_gem_payload_size: (int) This attribute defines the maximum payload
- size generated in the associated GEM interworking
- termination point managed entity. (0..65535
- """
- MEFrame.check_type(max_gem_payload_size, (int, type(None)))
- if max_gem_payload_size is not None and not 0 <= max_gem_payload_size <= 0xFFFF: # TODO: verify min/max
- raise ValueError('max_gem_payload_size should be 0..0xFFFF')
-
- data = None if max_gem_payload_size is None else\
- {
- 'max_gem_payload_size': max_gem_payload_size
- }
- super(GalEthernetProfileFrame, self).__init__(GalEthernetProfile,
- entity_id,
- data)
-
-
-class GemInterworkingTpFrame(MEFrame):
- """
- An instance of this managed entity represents a point in the ONU where the
- interworking of a bearer service (usually Ethernet) to the GEM layer takes
- place.
- """
- def __init__(self, entity_id,
- gem_port_network_ctp_pointer=None,
- interworking_option=None,
- service_profile_pointer=None,
- interworking_tp_pointer=None,
- pptp_counter=None,
- gal_profile_pointer=None,
- attributes=None):
- """
- :param entity_id: (int) This attribute uniquely identifies each instance of
- this managed entity. (0..65535)
-
- :param gem_port_network_ctp_pointer: (int) This attribute points to an instance of
- the GEM port network CTP. (0..65535)
-
- :param interworking_option: (int) This attribute identifies the type
- of non-GEM function that is being interworked.
- The options are:
- 0 Circuit-emulated TDM
- 1 MAC bridged LAN
- 2 Reserved
- 3 Reserved
- 4 Video return path
- 5 IEEE 802.1p mapper
- 6 Downstream broadcast
- 7 MPLS PW TDM service
-
- :param service_profile_pointer: (int) This attribute points to an instance of
- a service profile.
- CES service profile if interworking option = 0
- MAC bridge service profile if interworking option = 1
- Video return path service profile if interworking option = 4
- IEEE 802.1p mapper service profile if interworking option = 5
- Null pointer if interworking option = 6
- CES service profile if interworking option = 7
-
- :param interworking_tp_pointer: (int) This attribute is used for the circuit
- emulation service and IEEE 802.1p mapper
- service without a MAC bridge.
-
- :param gal_profile_pointer: (int) This attribute points to an instance of
- a service profile.
-
- :param attributes: (basestring, list, set, dict) additional ME attributes.
- not specifically specified as a parameter. For gets
- a string, list, or set can be provided. For create/set
- operations, a dictionary should be provided, for
- deletes None may be specified..
- """
- # Validate
- self.check_type(gem_port_network_ctp_pointer, (int, type(None)))
- self.check_type(interworking_option, (int, type(None)))
- self.check_type(service_profile_pointer, (int, type(None)))
- self.check_type(interworking_tp_pointer,(int, type(None)))
- self.check_type(pptp_counter,(int, type(None)))
- self.check_type(gal_profile_pointer, (int, type(None)))
-
- if gem_port_network_ctp_pointer is not None and not 0 <= gem_port_network_ctp_pointer <= 0xFFFE: # TODO: Verify max
- raise ValueError('gem_port_network_ctp_pointer should be 0..0xFFFE')
-
- if interworking_option is not None and not 0 <= interworking_option <= 7:
- raise ValueError('interworking_option should be 0..7')
-
- if service_profile_pointer is not None and not 0 <= service_profile_pointer <= 0xFFFE: # TODO: Verify max
- raise ValueError('service_profile_pointer should be 0..0xFFFE')
-
- if interworking_tp_pointer is not None and not 0 <= interworking_tp_pointer <= 0xFFFE: # TODO: Verify max
- raise ValueError('interworking_tp_pointer should be 0..0xFFFE')
-
- if pptp_counter is not None and not 0 <= pptp_counter <= 255: # TODO: Verify max
- raise ValueError('pptp_counter should be 0..255')
-
- if gal_profile_pointer is not None and not 0 <= gal_profile_pointer <= 0xFFFE: # TODO: Verify max
- raise ValueError('gal_profile_pointer should be 0..0xFFFE')
-
- data = MEFrame._attr_to_data(attributes)
-
- if gem_port_network_ctp_pointer is not None or \
- interworking_option is not None or \
- service_profile_pointer is not None or \
- interworking_tp_pointer is not None or \
- gal_profile_pointer is not None:
-
- data = data or dict()
-
- if gem_port_network_ctp_pointer is not None:
- data['gem_port_network_ctp_pointer'] = gem_port_network_ctp_pointer
-
- if interworking_option is not None:
- data['interworking_option'] = interworking_option
-
- if service_profile_pointer is not None:
- data['service_profile_pointer'] = service_profile_pointer
-
- if interworking_tp_pointer is not None:
- data['interworking_tp_pointer'] = interworking_tp_pointer
-
- if gal_profile_pointer is not None:
- data['gal_profile_pointer'] = gal_profile_pointer
-
- super(GemInterworkingTpFrame, self).__init__(GemInterworkingTp,
- entity_id,
- data)
-
-
-class GemPortNetworkCtpFrame(MEFrame):
- """
- This managed entity represents the termination of a GEM port on an ONU.
- """
- def __init__(self, entity_id, port_id=None, tcont_id=None,
- direction=None, upstream_tm=None, attributes=None):
- """
- :param entity_id: (int) This attribute uniquely identifies each instance of
- this managed entity. (0..65535)
-
- :param port_id: (int) This attribute is the port-ID of the GEM port associated
- with this CTP
-
- :param tcont_id: (int) This attribute points to a T-CONT instance
-
- :param direction: (string) Data direction. Valid values are:
- 'upstream' - UNI-to-ANI
- 'downstream' - ANI-to-UNI
- 'bi-directional' - guess :-)
-
- :param upstream_tm: (int) If the traffic management option attribute in
- the ONU-G ME is 0 (priority controlled) or 2
- (priority and rate controlled), this pointer
- specifies the priority queue ME serving this GEM
- port network CTP. If the traffic management
- option attribute is 1 (rate controlled), this
- attribute redundantly points to the T-CONT serving
- this GEM port network CTP.
-
- :param attributes: (basestring, list, set, dict) additional ME attributes.
- not specifically specified as a parameter. For gets
- a string, list, or set can be provided. For create/set
- operations, a dictionary should be provided, for
- deletes None may be specified.
- """
- _directions = {"upstream": 1, "downstream": 2, "bi-directional": 3}
-
- # Validate
- self.check_type(port_id, (int, type(None)))
- self.check_type(tcont_id, (int, type(None)))
- self.check_type(direction, (basestring, type(None)))
- self.check_type(upstream_tm, (int, type(None)))
-
- if port_id is not None and not 0 <= port_id <= 0xFFFE: # TODO: Verify max
- raise ValueError('port_id should be 0..0xFFFE')
-
- if tcont_id is not None and not 0 <= tcont_id <= 0xFFFE: # TODO: Verify max
- raise ValueError('tcont_id should be 0..0xFFFE')
-
- if direction is not None and str(direction).lower() not in _directions:
- raise ValueError('direction should one of {}'.format(_directions.keys()))
-
- if upstream_tm is not None and not 0 <= upstream_tm <= 0xFFFE: # TODO: Verify max
- raise ValueError('upstream_tm should be 0..0xFFFE')
-
- data = MEFrame._attr_to_data(attributes)
-
- if port_id is not None or tcont_id is not None or\
- direction is not None or upstream_tm is not None:
-
- data = data or dict()
-
- if port_id is not None:
- data['port_id'] = port_id
- if tcont_id is not None:
- data['tcont_pointer'] = tcont_id
- if direction is not None:
- data['direction'] = _directions[str(direction).lower()]
- if upstream_tm is not None:
- data['traffic_management_pointer_upstream'] = upstream_tm
-
- super(GemPortNetworkCtpFrame, self).__init__(GemPortNetworkCtp,
- entity_id,
- data)
-
-
-class Ieee8021pMapperServiceProfileFrame(MEFrame):
- """
- This managed entity associates the priorities of IEEE 802.1p [IEEE
- 802.1D] priority tagged frames with specific connections.
- """
- def __init__(self, entity_id, tp_pointer=None, interwork_tp_pointers=None):
- """
- :param entity_id: (int) This attribute uniquely identifies each instance of
- this managed entity. (0..65535)
-
- :param tp_pointer: (int) This attribute points to an instance of the
- associated termination point. (0..65535)
-
- :param interwork_tp_pointers: (list) List of 1 to 8 interworking termination
- point IDs. The first entry is assigned
- got p-bit priority 0. If less than 8 IDs
- are provided, the last ID is used for
- the remaining items.
- """
- if tp_pointer is None and interwork_tp_pointers is None:
- data = dict(
- tp_pointer=OmciNullPointer,
- interwork_tp_pointer_for_p_bit_priority_0=OmciNullPointer,
- interwork_tp_pointer_for_p_bit_priority_1=OmciNullPointer,
- interwork_tp_pointer_for_p_bit_priority_2=OmciNullPointer,
- interwork_tp_pointer_for_p_bit_priority_3=OmciNullPointer,
- interwork_tp_pointer_for_p_bit_priority_4=OmciNullPointer,
- interwork_tp_pointer_for_p_bit_priority_5=OmciNullPointer,
- interwork_tp_pointer_for_p_bit_priority_6=OmciNullPointer,
- interwork_tp_pointer_for_p_bit_priority_7=OmciNullPointer
- )
- else:
- self.check_type(tp_pointer, (list, type(None)))
- self.check_type(interwork_tp_pointers, (list, type(None)))
-
- data = dict()
-
- if tp_pointer is not None:
- data['tp_pointer'] = tp_pointer
-
- if interwork_tp_pointers is not None:
- assert all(isinstance(tp, int) and 0 <= tp <= 0xFFFF
- for tp in interwork_tp_pointers),\
- 'Interworking TP IDs must be 0..0xFFFF'
- assert 1 <= len(interwork_tp_pointers) <= 8, \
- 'Invalid number of Interworking TP IDs. Must be 1..8'
-
- data = dict()
- for pbit in range(0, len(interwork_tp_pointers)):
- data['interwork_tp_pointer_for_p_bit_priority_{}'.format(pbit)] = \
- interwork_tp_pointers[pbit]
-
- for pbit in range(len(interwork_tp_pointers), 7):
- data['interwork_tp_pointer_for_p_bit_priority_{}'.format(pbit)] = \
- interwork_tp_pointers[len(interwork_tp_pointers) - 1]
-
- super(Ieee8021pMapperServiceProfileFrame, self).__init__(Ieee8021pMapperServiceProfile,
- entity_id,
- data)
-
-
-class MacBridgePortConfigurationDataFrame(MEFrame):
- """
- This managed entity represents the ONU as equipment.
- """
- def __init__(self, entity_id, bridge_id_pointer=None, port_num=None,
- tp_type=None, tp_pointer=None, attributes=None):
- """
- :param entity_id: (int) This attribute uniquely identifies each instance of
- this managed entity. (0..65535)
-
- :param bridge_id_pointer: (int) This attribute points to an instance of the
- MAC bridge service profile. (0..65535)
-
- :param port_num: (int) This attribute is the bridge port number. (0..255)
-
- :param tp_type: (int) This attribute identifies the type of termination point
- associated with this MAC bridge port. Valid values are:
- 1 Physical path termination point Ethernet UNI
- 2 Interworking VCC termination point
- 3 IEEE 802.1p mapper service profile
- 4 IP host config data or IPv6 host config data
- 5 GEM interworking termination point
- 6 Multicast GEM interworking termination point
- 7 Physical path termination point xDSL UNI part 1
- 8 Physical path termination point VDSL UNI
- 9 Ethernet flow termination point
- 10 Reserved
- 11 Virtual Ethernet interface point
- 12 Physical path termination point MoCA UNI
-
- :param tp_pointer: (int) This attribute points to the termination point
- associated with this MAC bridge por. (0..65535)
-
- :param attributes: (basestring, list, set, dict) additional ME attributes.
- not specifically specified as a parameter. For gets
- a string, list, or set can be provided. For create/set
- operations, a dictionary should be provided, for
- deletes None may be specified.
- """
- # Validate
- self.check_type(bridge_id_pointer, (int, type(None)))
- self.check_type(port_num, (int, type(None)))
- self.check_type(tp_type, (int, type(None)))
- self.check_type(tp_pointer, (int, type(None)))
-
- if bridge_id_pointer is not None and not 0 <= bridge_id_pointer <= 0xFFFE: # TODO: Verify max
- raise ValueError('bridge_id_pointer should be 0..0xFFFE')
-
- if port_num is not None and not 0 <= port_num <= 255:
- raise ValueError('port_num should be 0..255') # TODO: Verify min,max
-
- if tp_type is not None and not 1 <= tp_type <= 12:
- raise ValueError('service_profile_pointer should be 1..12')
-
- if tp_pointer is not None and not 0 <= tp_pointer <= 0xFFFE: # TODO: Verify max
- raise ValueError('interworking_tp_pointer should be 0..0xFFFE')
-
- data = MEFrame._attr_to_data(attributes)
-
- if bridge_id_pointer is not None or \
- port_num is not None or \
- tp_type is not None or \
- tp_pointer is not None:
-
- data = data or dict()
-
- if bridge_id_pointer is not None:
- data['bridge_id_pointer'] = bridge_id_pointer
-
- if port_num is not None:
- data['port_num'] = port_num
-
- if tp_type is not None:
- data['tp_type'] = tp_type
-
- if tp_pointer is not None:
- data['tp_pointer'] = tp_pointer
-
- super(MacBridgePortConfigurationDataFrame, self).\
- __init__(MacBridgePortConfigurationData, entity_id, data)
-
-
-class MacBridgeServiceProfileFrame(MEFrame):
- """
- This managed entity models a MAC bridge in its entirety; any number
- of ports may be associated with the bridge through pointers to the
- MAC bridge service profile managed entity.
- """
- def __init__(self, entity_id, attributes=None):
- """
- :param entity_id: (int) This attribute uniquely identifies each instance of
- this managed entity. (0..65535)
-
- :param attributes: (basestring, list, set, dict) attributes. For gets
- a string, list, or set can be provided. For create/set
- operations, a dictionary should be provided, for
- deletes None may be specified.
- """
- super(MacBridgeServiceProfileFrame, self).__init__(MacBridgeServiceProfile,
- entity_id,
- MEFrame._attr_to_data(attributes))
-
-
-class OntGFrame(MEFrame):
- """
- This managed entity represents the ONU as equipment.
- """
- def __init__(self, attributes=None):
- """
- :param attributes: (basestring, list, set, dict) attributes. For gets
- a string, list, or set can be provided. For create/set
- operations, a dictionary should be provided, for
- deletes None may be specified.
- """
- super(OntGFrame, self).__init__(OntG, 0,
- MEFrame._attr_to_data(attributes))
-
-
-class Ont2GFrame(MEFrame):
- """
- This managed entity contains additional attributes associated with a PON ONU.
- """
- def __init__(self, attributes=None):
- """
- :param attributes: (basestring, list, set, dict) attributes. For gets
- a string, list, or set can be provided. For create/set
- operations, a dictionary should be provided, for
- deletes None may be specified.
- """
- # Only one managed entity instance (Entity ID=0)
- super(Ont2GFrame, self).__init__(Ont2G, 0,
- MEFrame._attr_to_data(attributes))
-
-
-class PptpEthernetUniFrame(MEFrame):
- """
- This managed entity represents the point at an Ethernet UNI where the physical path
- terminates and Ethernet physical level functions are performed.
- """
- def __init__(self, entity_id, attributes=None):
- """
- :param entity_id: (int) This attribute uniquely identifies each instance of
- this managed entity. (0..65535)
-
- :param attributes: (basestring, list, set, dict) attributes. For gets
- a string, list, or set can be provided. For create/set
- operations, a dictionary should be provided, for
- deletes None may be specified.
- """
- super(PptpEthernetUniFrame, self).__init__(PptpEthernetUni, entity_id,
- MEFrame._attr_to_data(attributes))
-
-
-class SoftwareImageFrame(MEFrame):
- """
- This managed entity models an executable software image stored in the ONU.
- """
- def __init__(self, entity_id):
- """
- :param entity_id: (int) This attribute uniquely identifies each instance of
- this managed entity. (0..65535)
- """
- super(SoftwareImageFrame, self).__init__(SoftwareImage, entity_id, None)
-
-
-class TcontFrame(MEFrame):
- """
- An instance of the traffic container managed entity T-CONT represents a
- logical connection group associated with a G-PON PLOAM layer alloc-ID.
- """
- def __init__(self, entity_id, alloc_id=None, policy=None):
- """
- :param entity_id: (int) This attribute uniquely identifies each instance of
- this managed entity. (0..65535)
-
- :param alloc_id: (int) This attribute links the T-CONT with the alloc-ID
- assigned by the OLT in the assign_alloc-ID PLOAM
- message (0..0xFFF)
-
- :param policy: (int) This attribute indicates the T-CONT's traffic scheduling
- policy. Valid values:
- 0 - Null
- 1 - Strict priority
- 2 - WRR - Weighted round robin
- """
- # Validate
- self.check_type(alloc_id, (int, type(None)))
- self.check_type(policy, (int, type(None)))
-
- if alloc_id is not None and not 0 <= alloc_id <= 0xFFF:
- raise ValueError('alloc_id should be 0..0xFFF')
-
- if policy is not None and not 0 <= policy <= 2:
- raise ValueError('policy should be 0..2')
-
- if alloc_id is None and policy is None:
- data = None
- else:
- data = dict()
-
- if alloc_id is not None:
- data['alloc_id'] = alloc_id
-
- if policy is not None:
- data['policy'] = policy
-
- super(TcontFrame, self).__init__(Tcont, entity_id, data)
-
-
-class VlanTaggingFilterDataFrame(MEFrame):
- """
- An instance of this managed entity represents a point in the ONU where the
- interworking of a bearer service (usually Ethernet) to the GEM layer takes
- place.
- """
- def __init__(self, entity_id, vlan_tcis=None, forward_operation=None):
- """
- :param entity_id: (int) This attribute uniquely identifies each instance of
- this managed entity. (0..65535)
-
- :param vlan_tcis: (list) This attribute is a list of provisioned TCI values
- for the bridge port. (0..0xFFFF)
-
- :param forward_operation: (int) What to do. See ITU spec for more information
-
- """
- # Validate
- self.check_type(vlan_tcis, (list, type(None)))
- self.check_type(forward_operation, (int, type(None)))
-
- if forward_operation is not None and not 0 <= forward_operation <= 0x21:
- raise ValueError('forward_operation should be 0..0x21')
-
- if vlan_tcis is None and forward_operation is None:
- data = None
-
- else:
- data = dict()
-
- if vlan_tcis is not None:
- assert all(isinstance(tci, int) and 0 <= tci <= 0xFFFF
- for tci in vlan_tcis), "VLAN TCI's are 0..0xFFFF"
- assert 1 <= len(vlan_tcis) <= 12, 'Number of VLAN TCI values is 1..12'
-
- for index in range(0, len(vlan_tcis)):
- data['vlan_filter_{}'.format(index)] = vlan_tcis[index]
-
- data['number_of_entries'] = len(vlan_tcis)
-
- if forward_operation is not None:
- assert 0 <= forward_operation <= 0x21, 'forwarding_operation must be 0x00..0x21'
- data['forward_operation'] = forward_operation
-
- super(VlanTaggingFilterDataFrame, self).__init__(VlanTaggingFilterData,
- entity_id,
- data)
-
-
-class OntDataFrame(MEFrame):
- """
- This managed entity models the MIB itself
- """
- def __init__(self, mib_data_sync=None):
- """
- :param mib_data_sync: (int) This attribute is used to check the alignment
- of the MIB of the ONU with the corresponding MIB
- in the OLT. (0..255)
- """
- self.check_type(mib_data_sync, (int, type(None)))
- if mib_data_sync is not None and not 0 <= mib_data_sync <= 255:
- raise ValueError('mib_data_sync should be 0..255')
-
- data = {'mib_data_sync': mib_data_sync} if mib_data_sync is not None else None
-
- super(OntDataFrame, self).__init__(OntData, 0, data)
diff --git a/voltha/adapters/adtran_onu/onu_gem_port.py b/voltha/adapters/adtran_onu/onu_gem_port.py
index c0fb759..9d5bc61 100644
--- a/voltha/adapters/adtran_onu/onu_gem_port.py
+++ b/voltha/adapters/adtran_onu/onu_gem_port.py
@@ -16,20 +16,21 @@
import structlog
from voltha.adapters.adtran_olt.xpon.gem_port import GemPort
from twisted.internet.defer import inlineCallbacks, returnValue, succeed
-from omci.omci_me import GemPortNetworkCtpFrame
+from voltha.extensions.omci.omci_me import GemPortNetworkCtpFrame, GemInterworkingTpFrame
class OnuGemPort(GemPort):
"""
Adtran ONU specific implementation
"""
- def __init__(self, gem_id, alloc_id,
+ def __init__(self, gem_id, alloc_id, entity_id,
encryption=False,
omci_transport=False,
multicast=False,
tcont_ref=None,
traffic_class=None,
intf_ref=None,
+ untagged=False,
exception=False, # FIXED_ONU
name=None,
handler=None,
@@ -41,13 +42,19 @@
tcont_ref=tcont_ref,
traffic_class=traffic_class,
intf_ref=intf_ref,
+ untagged=untagged,
exception=exception,
name=name,
handler=handler)
self._is_mock = is_mock
+ self._entity_id = entity_id
self.log = structlog.get_logger(device_id=handler.device_id, gem_id=gem_id)
@property
+ def entity_id(self):
+ return self._entity_id
+
+ @property
def encryption(self):
return self._encryption
@@ -57,102 +64,102 @@
if self._encryption != value:
self._encryption = value
- omci = None # TODO: Get from handler
@staticmethod
- def create(handler, gem_port, is_mock=False):
+ def create(handler, gem_port, entity_id, is_mock=False):
+
return OnuGemPort(gem_port['gemport-id'],
None,
+ entity_id,
encryption=gem_port['encryption'], # aes_indicator,
tcont_ref=gem_port['tcont-ref'],
name=gem_port['name'],
traffic_class=gem_port['traffic-class'],
handler=handler,
+ untagged='untagged' in gem_port['name'].lower(),
is_mock=is_mock)
@inlineCallbacks
- def add_to_hardware(self, omci):
+ def add_to_hardware(self, omci,
+ tcont_entity_id,
+ ieee_mapper_service_profile_entity_id,
+ gal_enet_profile_entity_id):
+ self.log.debug('add-to-hardware', gem_id=self.gem_id,
+ tcont_entity_id=tcont_entity_id,
+ ieee_mapper_service_profile_entity_id=ieee_mapper_service_profile_entity_id,
+ gal_enet_profile_entity_id=gal_enet_profile_entity_id)
if self._is_mock:
returnValue('mock')
- omci = self._handler.omci
- tcont = self.tcont
- assert omci is not None, 'No OMCI engine'
- assert tcont is not None, 'No TCONT'
- assert tcont.entity_id == 0x8001, 'Hardcoded Entity ID NOT FOUND'
-
try:
direction = "downstream" if self.multicast else "bi-directional"
assert not self.multicast, 'MCAST is not supported yet'
- # TODO: For TCONT ID, get the TCONT's entity ID that you programmed
- # TODO: For TM, is this the entity ID for a traffic descriptor?
- # results = yield omci.send_create_gem_port_network_ctp(self.gem_id, # Entity ID
- # self.gem_id, # Port ID
- # tcont.entity_id, # TCONT ID
- # direction, # Direction
- # 0x100) # TM
- results = None
- # results = yield omci.send(GemPortNetworkCtpFrame(self.gem_id, # Entity ID
- # self.gem_id, # Port ID
- # tcont.entity_id, # TCONT ID
- # direction, # Direction
- # 0x100).create() # TM
+ frame = GemPortNetworkCtpFrame(
+ self.entity_id, # same entity id as GEM port
+ port_id=self.gem_id,
+ tcont_id=tcont_entity_id,
+ direction=direction,
+ upstream_tm=0x8000 # TM ID, 32768 unique ID set in TD set TODO: Parameterize
+ ).create()
+ results = yield omci.send(frame)
+
+ status = results.fields['omci_message'].fields['success_code']
+ error_mask = results.fields['omci_message'].fields['parameter_error_attributes_mask']
+ self.log.debug('create-gem-port-network-ctp', status=status, error_mask=error_mask)
except Exception as e:
self.log.exception('gemport-create', e=e)
raise
try:
- # GEM Interworking config
- # TODO: For service mapper ID, always hardcoded or does it come from somewhere else
- # It is probably the TCONT entity ID
- results = None
- # results = yield omci.send_create_gem_inteworking_tp(self.gem_id, # Entity ID
- # self.gem_id, # GEMPort NET CTP ID
- # tcont.entity_id) # Service Mapper Profile ID
+ frame = GemInterworkingTpFrame(
+ self.entity_id, # same entity id as GEM port
+ gem_port_network_ctp_pointer=self.entity_id,
+ interworking_option=5, # IEEE 802.1
+ service_profile_pointer=ieee_mapper_service_profile_entity_id,
+ interworking_tp_pointer=0x0,
+ pptp_counter=1,
+ gal_profile_pointer=gal_enet_profile_entity_id
+ ).create()
+ results = yield omci.send(frame)
+
+ status = results.fields['omci_message'].fields['success_code']
+ error_mask = results.fields['omci_message'].fields['parameter_error_attributes_mask']
+ self.log.debug('create-gem-interworking-tp', status=status, error_mask=error_mask)
+
except Exception as e:
self.log.exception('interworking-create', e=e)
raise
- try:
- # Mapper Service Profile config
- # TODO: All p-bits currently go to the one and only GEMPORT ID for now
- # TODO: The entity ID is probably the TCONT entity ID
- results = None
- # results = omci.send_set_8021p_mapper_service_profile(tcont.entity_id, # Entity ID
- # self.gem_id) # Interworking TP ID
- except Exception as e:
- self.log.exception('mapper-set', e=e)
- raise
-
returnValue(results)
@inlineCallbacks
def remove_from_hardware(self, omci):
+ self.log.debug('remove-from-hardware', gem_id=self.gem_id)
if self._is_mock:
returnValue('mock')
- omci = self._handler.omci
- assert omci is not None, 'No OMCI engine'
+ try:
+ frame = GemInterworkingTpFrame(self.entity_id).delete()
+ results = yield omci.send(frame)
- results = succeed('TODO: Implement me')
+ status = results.fields['omci_message'].fields['success_code']
+ self.log.debug('delete-gem-interworking-tp', status=status)
- # uri = AdtranOltHandler.GPON_GEM_CONFIG_URI.format(pon_id, onu_id, self.gem_id)
- # name = 'gem-port-delete-{}-{}: {}'.format(pon_id, onu_id, self.gem_id)
- # return session.request('DELETE', uri, name=name)
+ except Exception as e:
+ self.log.exception('interworking-delete', e=e)
+ raise
+
+ try:
+ frame = GemPortNetworkCtpFrame(self.entity_id).delete()
+ results = yield omci.send(frame)
+
+ status = results.fields['omci_message'].fields['success_code']
+ self.log.debug('delete-gem-port-network-ctp', status=status)
+
+ except Exception as e:
+ self.log.exception('gemport-delete', e=e)
+ raise
+
returnValue(results)
-
- def set_config(self, omci, value, leaf):
- if self._is_mock:
- return
-
- # from ..adtran_olt_handler import AdtranOltHandler
- #
- # data = json.dumps({leaf: value})
- # uri = AdtranOltHandler.GPON_GEM_CONFIG_URI.format(self.pon_id,
- # self.onu_id,
- # self.gem_id)
- # name = 'onu-set-config-{}-{}-{}'.format(self._pon_id, leaf, str(value))
- # return session.request('PATCH', uri, data=data, name=name)
- pass # TODO: Implement me
diff --git a/voltha/adapters/adtran_onu/onu_tcont.py b/voltha/adapters/adtran_onu/onu_tcont.py
index 3d3e2bf..e926081 100644
--- a/voltha/adapters/adtran_onu/onu_tcont.py
+++ b/voltha/adapters/adtran_onu/onu_tcont.py
@@ -17,20 +17,20 @@
from voltha.adapters.adtran_olt.xpon.tcont import TCont
from voltha.adapters.adtran_olt.xpon.traffic_descriptor import TrafficDescriptor
-from omci.omci_me import TcontFrame
+from voltha.extensions.omci.omci_me import TcontFrame
class OnuTCont(TCont):
"""
Adtran ONU specific implementation
"""
- def __init__(self, handler, alloc_id, traffic_descriptor, entity_id,
+ def __init__(self, handler, alloc_id, traffic_descriptor,
name=None, vont_ani=None, is_mock=False):
super(OnuTCont, self).__init__(alloc_id, traffic_descriptor,
name=name, vont_ani=vont_ani)
self._handler = handler
self._is_mock = is_mock
- self._entity_id = entity_id
+ self._entity_id = None
self.log = structlog.get_logger(device_id=handler.device_id, alloc_id=alloc_id)
@property
@@ -42,33 +42,31 @@
assert isinstance(tcont, dict), 'TCONT should be a dictionary'
assert isinstance(td, TrafficDescriptor), 'Invalid Traffic Descriptor data type'
- # TODO: Pass in a unique TCONT Entity ID from the ONU's PON Object
- entity_id = 0x8001
-
return OnuTCont(handler,
tcont['alloc-id'],
td,
- entity_id,
name=tcont['name'],
vont_ani=tcont['vont-ani'],
is_mock=is_mock)
@inlineCallbacks
- def add_to_hardware(self, omci):
+ def add_to_hardware(self, omci, tcont_entity_id):
+ self.log.debug('add-to-hardware', tcont_entity_id=tcont_entity_id)
+
+ self._entity_id = tcont_entity_id
if self._is_mock:
returnValue('mock')
try:
- # TODO: What is a valid Entity ID (compute and save if needed)
- #
- # NOTE: Entity ID should be computed. For NGPON2, they were starting
- # at 256 and incrementing.
- results = None
- # results = yield self._handler.omci.send_set_tcont(self._entity_id, # Entity ID
- # self.alloc_id) # Alloc ID
+ frame = TcontFrame(self.entity_id, self.alloc_id).set()
+ results = yield omci.send(frame)
- # response = yield omci.send(TcontFrame(self._entity_id,
- # alloc_id=self.alloc_id).get())
+ 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-tcont', status=status,
+ failed_attributes_mask=failed_attributes_mask,
+ unsupported_attributes_mask=unsupported_attributes_mask)
except Exception as e:
self.log.exception('tcont-set', e=e)
@@ -78,20 +76,20 @@
@inlineCallbacks
def remove_from_hardware(self, omci):
+ self.log.debug('remove-from-hardware', tcont_entity_id=self.entity_id)
if self._is_mock:
returnValue('mock')
- results = None
- # results = yield omci.send(TcontFrame(self._entity_id).delete())
+ # Release tcont by setting alloc_id=0xFFFF
+ try:
+ frame = TcontFrame(self.entity_id, 0xFFFF).set()
+ results = yield omci.send(frame)
+
+ status = results.fields['omci_message'].fields['success_code']
+ self.log.debug('delete-tcont', status=status)
+
+ except Exception as e:
+ self.log.exception('tcont-delete', e=e)
+ raise
+
returnValue(results)
-
-
-
-
-
-
-
-
-
-
-
diff --git a/voltha/adapters/adtran_onu/pon_port.py b/voltha/adapters/adtran_onu/pon_port.py
index b9c82ba..5cc8ff9 100644
--- a/voltha/adapters/adtran_onu/pon_port.py
+++ b/voltha/adapters/adtran_onu/pon_port.py
@@ -13,7 +13,7 @@
# limitations under the License.
import structlog
-from twisted.internet.defer import inlineCallbacks, returnValue, succeed, TimeoutError
+from twisted.internet.defer import inlineCallbacks, returnValue, TimeoutError
from twisted.internet import reactor
from voltha.protos.common_pb2 import AdminState
@@ -21,38 +21,31 @@
from voltha.protos.common_pb2 import OperStatus, ConnectStatus
-from omci.omci_me import *
-from omci.deprecated import * # TODO: Remove this once OMCI_CC and ME_Frame refactoring is complete
-
-###################################################################################
-#
-# TODO: Notes -> This version is the fifth attempt. All calls converted with the
-# exception of the mib-reset and upload.
-#
-# Saving this off before moving things around.
-#
-#
-###################################################################################
-
+from omci.omci_entities import onu_custom_me_entities
+from voltha.extensions.omci.omci_me import *
_STARTUP_RETRY_WAIT = 5
-BRDCM_DEFAULT_VLAN = 4091 # TODO: Deprecate later...
-
# abbreviations
OP = EntityOperations
class PonPort(object):
"""Wraps northbound-port / vlan bridge support for ONU"""
+ MIN_GEM_ENTITY_ID = 0x4900
+ MAX_GEM_ENTITY_ID = 0x4AFF
def __init__(self, handler, port_no):
self._enabled = False
self._valid = True
self._handler = handler
self._deferred = None
+ self._resync_deferred = None # For TCont/GEM use
self._port = None
self._port_number = port_no
+ self._connected = False
+ self._dev_info_loaded = False
self._bridge_initialized = False
+ self._next_entity_id = PonPort.MIN_GEM_ENTITY_ID
self.log = structlog.get_logger(device_id=handler.device_id, port_no=port_no)
self._admin_state = AdminState.ENABLED
@@ -60,13 +53,14 @@
self._gem_ports = {} # gem-id -> GemPort
self._tconts = {} # alloc-id -> TCont
+ self._in_sync_subscription = None
+ self._connectivity_subscription = None
- # TODO: Until we have an external database, just save it here
- self.mib_data_store = dict() # TODO: Improve and make class attribute/property
+ self._onu_omci_device = handler.omci_agent.add_device(handler.device_id,
+ handler.adapter_agent,
+ onu_custom_me_entities())
# TODO: Add stats, alarm reference, ...
- pass
-
def __str__(self):
return "PonPort" # TODO: Encode current state
@@ -82,16 +76,24 @@
self._oper_status = OperStatus.ACTIVE
self._update_adapter_agent()
- # Begin ONU Activation sequence
- self._deferred = reactor.callLater(_STARTUP_RETRY_WAIT,
- self._initial_message_exchange)
- # TODO: start h/w sync
- pass
+ # Subscriber to events of interest in OpenOMCI
+ self._subscribe_to_events()
+ self._onu_omci_device.start()
+
+ # Begin ONU Activation sequence if already in sync
+ if self._onu_omci_device.mib_db_in_sync:
+ self._deferred = reactor.callLater(0, self._mib_in_sync)
+ else:
+ device = self._handler.adapter_agent.get_device(self._handler.device_id)
+ device.reason = 'Waiting for MIB upload completion'
+ self._handler.adapter_agent.update_device(device)
def _stop(self):
self._cancel_deferred()
+ # Unsubscribe to OpenOMCI Events
+ self._unsubscribe_to_events()
+ self._onu_omci_device.stop()
- self._bridge_initialized = False
self._admin_state = AdminState.DISABLED
self._oper_status = OperStatus.UNKNOWN
self._update_adapter_agent()
@@ -99,8 +101,18 @@
pass
def _cancel_deferred(self):
- d, self._deferred = self._deferred, None
+ d1, self._deferred = self._deferred, None
+ d2, self._resync_deferred = self._resync_deferred, None
+ for d in [d1, d2]:
+ try:
+ if d is not None and not d.called:
+ d.cancel()
+ except:
+ pass
+
+ def _cancel_resync_deferred(self):
+ d, self._resync_deferred = self._resync_deferred, None
try:
if d is not None and not d.called:
d.cancel()
@@ -128,13 +140,23 @@
self._stop()
@property
- def bridge_initialized(self):
- return self._bridge_initialized
+ def connected(self):
+ return self._connected
@property
def port_number(self):
return self._port_number
+ @property
+ def next_gem_entity_id(self):
+ entity_id = self._next_entity_id
+
+ self._next_entity_id = self._next_entity_id + 1
+ if self._next_entity_id > PonPort.MAX_GEM_ENTITY_ID:
+ self._next_entity_id = PonPort.MIN_GEM_ENTITY_ID
+
+ return entity_id
+
def get_port(self):
"""
Get the VOLTHA PORT object for this port
@@ -157,778 +179,477 @@
# self.adapter_agent.update_port(self.olt.device_id, self.get_port())
pass
- @inlineCallbacks
- def _initial_message_exchange(self):
- """
- Perform a MIB Reset and then collect some basic (read-only) attributes.
- Upon success, begin MIB upload sequence
- """
- self.log.info('initial-message-exchange')
- self._deferred = None
+ @property
+ def onu_omci_device(self):
+ return self._onu_omci_device
- if self._handler.device_id is None or not self.enabled:
- returnValue(succeed('deleted'))
+ def _mib_in_sync(self):
+ if self._handler.is_mock:
+ return # Done, Mock has no bridge support
- elif not self.enabled:
- # Wait until enabled
- returnValue('not-enabled')
+ if not self._dev_info_loaded:
+ # Here if in sync. But verify first
- omci = self._handler.omci
-
- try:
- # reset incoming message queue
- omci.flush()
-
- ####################################################
- # Start by getting some useful device information
+ omci = self._onu_omci_device
+ in_sync = omci.mib_db_in_sync
+ self.log.info('mib-in-sync', in_sync=in_sync, already_loaded=self._dev_info_loaded)
device = self._handler.adapter_agent.get_device(self._handler.device_id)
- device.oper_status = OperStatus.ACTIVATING
- device.connect_status = ConnectStatus.UNREACHABLE
- device.reason = 'Initial OMCI message exchange in progress'
-
- except Exception as e:
- self.log.exception('top-of-msg-exch', e=e)
- device = None
-
- if device is None:
- # Wait until enabled
- returnValue('no-device')
-
- try:
- # Note: May timeout to ONU not fully discovered (can happen in xPON case)
- # or other errors.
- # Decode fields in response and update device info
-
- response = yield omci.send(OntGFrame('vendor_id').get())
- # TODO: Get status for this and others below before getting other values...
- omci_response = response.getfieldval("omci_message")
- data = omci_response.getfieldval("data")
- vendor = data["vendor_id"]
- assert vendor == 'ADTN', \
- "Invalid Device/Wrong device adapter assigned: '{}'".format(vendor)
-
- # TODO: Get serial number and validate!
-
- # Mark as reachable if at least first message gets through
- device.connect_status = ConnectStatus.REACHABLE
- self._handler.adapter_agent.update_device(device)
-
- response = yield omci.send(CardholderFrame(True, 1,
- 'actual_plugin_unit_type').get())
-
- omci_response = response.getfieldval("omci_message")
- data = omci_response.getfieldval("data")
- # device.type = str(data["actual_plugin_unit_type"])
-
- response = yield omci.send(CircuitPackFrame(257, 'number_of_ports').get())
-
- omci_response = response.getfieldval("omci_message")
- data = omci_response.getfieldval("data")
- num_ports = data["number_of_ports"]
- assert num_ports == 1, 'Invalid number of ports: {}'.format(num_ports)
-
- response = yield omci.send(IpHostConfigDataFrame(515, 'mac_address').get())
-
- omci_response = response.getfieldval("omci_message")
- data = omci_response.getfieldval("data")
- device.mac_address = str(data["mac_address"])
-
- response = yield omci.send(Ont2GFrame('equipment_id').get())
-
- omci_response = response.getfieldval("omci_message")
- data = omci_response.getfieldval("data")
- eqptId_bootVersion = str(data["equipment_id"])
- eqptId = eqptId_bootVersion[0:10] # ie) BVMDZ10DRA
- bootVersion = eqptId_bootVersion[12:20] # ie) CML.D55~
-
- # response = yield omci.send_get_Ont2G('omcc_version', 0)
- response = yield omci.send(Ont2GFrame('omcc_version').get())
-
- omci_response = response.getfieldval("omci_message")
- data = omci_response.getfieldval("data")
- # decimal version
- omciVersion = str(data["omcc_version"])
-
- response = yield omci.send(Ont2GFrame('vendor_product_code').get())
-
- omci_response = response.getfieldval("omci_message")
- data = omci_response.getfieldval("data")
- # decimal value
- vendorProductCode = str(data["vendor_product_code"])
-
- response = yield omci.send(OntGFrame('version').get())
-
- omci_response = response.getfieldval("omci_message")
- data = omci_response.getfieldval("data")
- device.model = str(data["version"]) # such as 1287800F1
-
- # TODO: Combine ONTG calls into a single call with multiple attributes
- # TODO: Combine ONT2G calls into a single call with multiple attributes
-
- # TODO: Look into ONTG and ONT2G to see if we can get other items of interest
- # such as max tconts, max gem ports, and so on. Make use of them
-
- # Possibility of bug in ONT Firmware. uncomment this code after it is fixed.
- # response = yield omci.send_get_SoftwareImage('version', 0)
- #
- # omci_response = response.getfieldval("omci_message")
- # data = omci_response.getfieldval("data")
- # device.firmware_version = str(data["version"])
- # is_committed = data["is_committed"]
- # is_active = data["is_active"]
- # is_valid = data["is_valid"]
-
- # TODO: May have some issue with the next one...
- # response = yield omci.send_set_adminState(257)
-
- # device.hardware_version = 'TODO: to be filled'
- # TODO: Support more versions as needed
- # images = Image(version=results.get('software_version', 'unknown'))
- # device.images.image.extend([images])
-
- # self.adapter_agent.update_device(device)
device.oper_status = OperStatus.ACTIVE
device.connect_status = ConnectStatus.REACHABLE
- device.reason = 'Initial OMCI message exchange complete'
+ device.reason = 'MIB Synchronization complete'
self._handler.adapter_agent.update_device(device)
- # Start MIB synchronization
- self._deferred = reactor.callLater(0, self._perform_mib_upload)
- self.log.info('onu-activated')
+ # In Sync, we can register logical ports now. Ideally this could occur on
+ # the first time we received a successful (no timeout) OMCI Rx response.
+ try:
+ for uni in self._handler.uni_ports:
+ uni.add_logical_port(None, None)
- # These exceptions are not recoverable
- except (TypeError, ValueError) as e:
- self.log.exception('Failed', e=e)
- device.oper_status = OperStatus.FAILED
- device.reason = 'Initial message sequence failure: ' + e.message
- self._handler.adapter_agent.update_device(device)
+ vendor = omci.query_mib_single_attribute(OntG.class_id, 0, 'vendor_id') or 'ADTN'
+ assert vendor == 'ADTN', \
+ "Invalid Device/Wrong device adapter assigned: '{}'".format(vendor)
- except TimeoutError as e:
- self.log.debug('Failed', e=e)
- self._handler.adapter_agent.update_device(device)
- # Try again later. May not have been discovered
- self._deferred = reactor.callLater(_STARTUP_RETRY_WAIT,
- self._initial_message_exchange)
+ # TODO: Get serial number and validate!
+ num_ports = omci.query_mib_single_attribute(CircuitPack.class_id,
+ 257, 'number_of_ports') or 1
+ assert num_ports == 1, 'Invalid number of ports: {}'.format(num_ports)
- except Exception as e:
- self.log.exception('Failed', e=e)
- device.reason = 'Initial message sequence failure: ' + e.message
- self._handler.adapter_agent.update_device(device)
- # Try again later. May not have been discovered
- self._deferred = reactor.callLater(_STARTUP_RETRY_WAIT,
- self._initial_message_exchange)
+ mac_address = omci.query_mib_single_attribute(IpHostConfigData.class_id,
+ 0, 'mac_address') or 'unknown'
+ device.mac_address = str(mac_address)
+
+ ont2_attributes = omci.query_mib(Ont2G.class_id, 0, ['equipment_id',
+ 'omcc_version',
+ 'vendor_product_code'])
+ equipment_id = ont2_attributes.get('equipment_id') or " unknown unknown "
+ eqptId_bootVersion = str(equipment_id)
+ eqptId = eqptId_bootVersion[0:10] # ie) BVMDZ10DRA
+ bootVersion = eqptId_bootVersion[12:20] # ie) CML.D55~
+
+ omcc_version = str(ont2_attributes.get('omcc_version', 'unknown'))
+ vendorProductCode = str(ont2_attributes.get('vendor_product_code', 'unknown'))
+
+ version = omci.query_mib_single_attribute(OntG.class_id, 0, 'version') or 'unknown'
+ device.model = str(version)
+ # # TODO: Combine ONTG calls into a single call with multiple attributes
+ # # TODO: Look into ONTG and ONT2G to see if we can get other items of interest
+ # # such as max tconts, max gem ports, and so on. Make use of them
+
+ sw_version = omci.query_mib_single_attribute(SoftwareImage.class_id, 0, 'version') or 'unknown'
+ device.firmware_version = str(sw_version)
+ # # is_committed = data["is_committed"]
+ # # is_active = data["is_active"]
+ # # is_valid = data["is_valid"]
+ # # device.hardware_version = 'TODO: to be filled'
+ # # TODO: Support more versions as needed
+ # # images = Image(version=results.get('software_version', 'unknown'))
+ # # device.images.image.extend([images])
+
+ self._handler.adapter_agent.update_device(device)
+ self._dev_info_loaded = True
+
+ except Exception as e:
+ self.log.exception('device-info-load', e=e)
+ self._deferred = reactor.callLater(_STARTUP_RETRY_WAIT, self._mib_in_sync())
+
+ self._deferred = reactor.callLater(0, self.resync_omci_settings)
@inlineCallbacks
- def _perform_mib_upload(self):
- """
- Called after basic OMCI MIB RESET message startup/exchange.
+ def resync_omci_settings(self):
+ self._cancel_resync_deferred()
- Upon successful completion, proceed to establish a few basic structures
- that we know will be required. Once OpenOMCI is created, this sequence
- should be skipped (go directly to MIB upload) and this info is available
- from the uploaded MIB.
-
- On failure, restart the initial message exchange
- """
- self.log.info('perform-mib-upload')
- self._deferred = None
-
- if self._handler.device_id is None or not self.enabled:
- returnValue('not-enabled')
-
- device = None
- seq_no = 0
- number_of_commands = 0
- omci = self._handler.omci
-
- if self._handler.is_mock:
- self._deferred = reactor.callLater(0, self._perform_mib_download)
- returnValue('is-mock')
-
- try:
+ if not self._bridge_initialized:
+ self.log.info('resync-omci-settings', initialized=self._bridge_initialized)
device = self._handler.adapter_agent.get_device(self._handler.device_id)
- device.reason = 'Performing MIB Synchronization'
+
+ if not self.enabled or device is None:
+ returnValue('not-enabled')
+
+ device.reason = 'Performing OMCI Setup'
self._handler.adapter_agent.update_device(device)
- #########################################
- # MIB Reset
- results = yield omci.send_mib_reset()
- status = results.fields['omci_message'].fields['success_code']
- assert status == 0, 'Unexpected MIB reset response status: {}'.format(status)
- # TODO: On a real system, need to flush the external MIB database
- # TODO: Also would need to watch for any AVC being handled between the MIB reset and the DB flush
- self.mib_data_store = dict()
+ omci = self._handler.omci
- ########################################
- # Begin MIB Upload
- results = yield omci.send_mib_upload()
- number_of_commands = results.fields['omci_message'].fields['number_of_commands']
+ #############################################
+ # All our variables here
+ # TODO: Move elsewhere in future version of this software
+ # TODO: Make as many entity IDs dynamic/discovered as possible
+ frame = None
+ gal_enet_profile_entity_id = 0x100
+ ieee_mapper_service_profile_entity_id = 0x100
+ mac_bridge_service_profile_entity_id = 0x100
+ mac_bridge_port_ani_entity_id = 0x100
+ ethernet_uni_entity_id = 0x101
+ vlan_tcis_1 = 0x900
+ vlan_config_entity_id = vlan_tcis_1
+ cvid = device.vlan
- for seq_no in xrange(number_of_commands):
- results = yield omci.send_mib_upload_next(seq_no)
+ try:
+ ################################################################################
+ # TCONTS
+ # get tconts in database
+ omci_dev = self._onu_omci_device
+ tcont_idents = omci_dev.query_mib(Tcont.class_id)
+ self.log.debug('tcont-idents', tcont_idents=tcont_idents)
- object_entity_class = results.fields['omci_message'].fields['object_entity_class']
- object_entity_id = results.fields['omci_message'].fields['object_entity_id']
- object_attributes_mask = results.fields['omci_message'].fields['object_attributes_mask']
- object_data = results.fields['omci_message'].fields['object_data']
+ for tcont in self._tconts.itervalues():
+ free_entity_id = next((k for k, v in tcont_idents.items()
+ if isinstance(k, int) and v.get('alloc_id', 0) == 0xFFFF), None)
+ if free_entity_id is None:
+ self.log.error('no-available-tconts')
+ break
+ results = yield tcont.add_to_hardware(omci, free_entity_id)
- key = (object_entity_class, object_entity_id)
+ ################################################################################
+ # GEMS
+ for gem_port in self._gem_ports.itervalues():
+ tcont = gem_port.tcont
+ if tcont is None:
+ self.log.error('unknown-tcont-reference', gem_id=gem_port.gem_id)
+ continue
- if key not in self.mib_data_store:
- self.mib_data_store[key] = (object_attributes_mask, object_data)
- else:
- pass
+ results = yield gem_port.add_to_hardware(omci,
+ tcont.entity_id,
+ ieee_mapper_service_profile_entity_id,
+ gal_enet_profile_entity_id)
- # Successful if here
- device.reason = 'MIB Synchronization Complete'
- self._handler.adapter_agent.update_device(device)
+ ########################################################################################
+ # Create GalEthernetProfile - Once per ONU/PON interface
+ #
+ # EntityID will be referenced by:
+ # - GEM Interworking TPs when a new GEM Port is created
+ # References:
+ # - Nothing
- # Start up non-critical message exchange
- self._deferred = reactor.callLater(0, self._perform_mib_download)
- self.log.info('mib-synchronized')
+ frame = GalEthernetProfileFrame(gal_enet_profile_entity_id,
+ max_gem_payload_size=1518).create() # Max GEM Payload size
+ results = yield omci.send(frame)
- except TimeoutError as e:
- self.log.warn('mib-upload', e=e, seq_no=seq_no, number_of_commands=number_of_commands)
+ status = results.fields['omci_message'].fields['success_code']
+ error_mask = results.fields['omci_message'].fields['parameter_error_attributes_mask']
+ self.log.debug('create-gal-ethernet-profile', status=status, error_mask=error_mask)
- if device is not None:
- device.reason = 'mib-upload-failure: Response Timeout'
+ ################################################################################
+ # MAC Bridge Service Profile - Once per UNI
+ #
+ # EntityID will be referenced by:
+ # - MAC Bridge Port Configuration Data
+ # References:
+ # - Nothing
+
+ attributes = {
+ 'spanning_tree_ind': False,
+ # TODO: CB: see if we need or can use any of the following...
+ # 'learning_ind': True,
+ # 'priority': 0x8000,
+ # 'max_age': 20 * 256,
+ # 'hello_time': 2 * 256,
+ # 'forward_delay': 15 * 256,
+ # 'unknown_mac_address_discard': True
+ }
+ frame = MacBridgeServiceProfileFrame(mac_bridge_service_profile_entity_id,
+ attributes).create()
+ results = yield omci.send(frame)
+
+ status = results.fields['omci_message'].fields['success_code']
+ error_mask = results.fields['omci_message'].fields['parameter_error_attributes_mask']
+ self.log.debug('create-mac-bridge-service-profile', status=status, error_mask=error_mask)
+
+ ################################################################################
+ # IEEE 802.1 Mapper Service config - Once per PON
+ #
+ # EntityID will be referenced by:
+ # - MAC Bridge Port Configuration Data for the PON port
+ # References:
+ # - Nothing at this point. When a GEM port is created, this entity will
+ # be updated to reference the GEM Interworking TP
+
+ frame = Ieee8021pMapperServiceProfileFrame(ieee_mapper_service_profile_entity_id).create()
+ results = yield omci.send(frame)
+
+ status = results.fields['omci_message'].fields['success_code']
+ error_mask = results.fields['omci_message'].fields['parameter_error_attributes_mask']
+ self.log.debug('create-8021p-mapper-service-profile', status=status, error_mask=error_mask)
+
+ ################################################################################
+ # Create MAC Bridge Port Configuration Data for the PON port via IEEE 802.1
+ # mapper service. Upon receipt by the ONU, the ONU will create an instance
+ # of the following before returning the response.
+ #
+ # - MAC bridge port designation data
+ # - MAC bridge port filter table data
+ # - MAC bridge port bridge table data
+ #
+ # EntityID will be referenced by:
+ # - Implicitly by the VLAN tagging filter data
+ # -
+ # -
+ # -
+ # References:
+ # - MAC Bridge Service Profile (the bridge)
+ # - IEEE 802.1p mapper service profile for PON port
+
+ frame = MacBridgePortConfigurationDataFrame(
+ mac_bridge_port_ani_entity_id, # Entity ID
+ bridge_id_pointer=mac_bridge_service_profile_entity_id, # Bridge Entity ID BP: oldvalue 0x201
+ # TODO: The PORT number for this port and the UNI port are the same. Is this correct?
+ port_num=0, # Port ID BP: oldvalue 2
+ tp_type=3, # TP Type (IEEE 802.1p mapper service) BP: oldvalue 1, 802.1 mapper GPON intf
+ tp_pointer=ieee_mapper_service_profile_entity_id # TP ID, 8021p mapper ID BP: oldvalue 0x102
+ ).create()
+ results = yield omci.send(frame)
+
+ status = results.fields['omci_message'].fields['success_code']
+ error_mask = results.fields['omci_message'].fields['parameter_error_attributes_mask']
+ self.log.debug('create-mac-bridge-port-configuration-data-part-1', status=status, error_mask=error_mask)
+
+ ################################################################################
+ # MAC Bridge Port config
+ # This configuration is for Ethernet UNI
+ #
+ # EntityID will be referenced by:
+ # -
+ # -
+ # -
+ # -
+ # References:
+ # - MAC Bridge Service Profile (the bridge)
+ # - PPTP Ethernet UNI
+
+ frame = MacBridgePortConfigurationDataFrame(
+ 0x000, # Entity ID BP: oldvalue 0x201
+ bridge_id_pointer=mac_bridge_service_profile_entity_id, # Bridge Entity ID BP: oldvalue 0x201
+ port_num=0, # Port ID BP: oldvalue 3
+ tp_type=1, # PPTP Ethernet UNI BP: oldvalue 3
+ tp_pointer=ethernet_uni_entity_id # TP ID, 8021p mapper Id BP: oldvalue 0x8001
+ ).create()
+ results = yield omci.send(frame)
+
+ status = results.fields['omci_message'].fields['success_code']
+ error_mask = results.fields['omci_message'].fields['parameter_error_attributes_mask']
+ self.log.debug('create-mac-bridge-port-configuration-data-part-2', status=status, error_mask=error_mask)
+
+ ################################################################################
+ # VLAN Tagging Filter config
+ #
+ # EntityID will be referenced by:
+ # - Nothing
+ # References:
+ # - Implicitly linked to an instance of the MAC bridge port configuration data
+ # for the PON port
+ # TODO: Probably need to get VLAN ID from device.vlan
+ # Set anything, this request will not be used when using Extended Vlan
+
+ frame = VlanTaggingFilterDataFrame(
+ mac_bridge_port_ani_entity_id, # Entity ID BP: Oldvalue 0x2102
+ vlan_tcis=[vlan_tcis_1], # VLAN IDs BP: cvid
+ forward_operation=0x10
+ ).create()
+ results = yield omci.send(frame)
+
+ status = results.fields['omci_message'].fields['success_code']
+ error_mask = results.fields['omci_message'].fields['parameter_error_attributes_mask']
+ self.log.debug('create-vlan-tagging-filter-data', status=status, error_mask=error_mask)
+
+ ################################################################################
+ # Update the IEEE 802.1p Mapper Service Profile config
+ #
+ # EntityID was created prior to this call
+ # References:
+ # -
+ # -
+ # TODO: All p-bits currently go to the one and only GEMPORT ID for now
+
+ gem_entity_ids = []
+ for gem_port in self._gem_ports.itervalues():
+ gem_entity_ids.append(gem_port.entity_id)
+
+ frame = Ieee8021pMapperServiceProfileFrame(
+ ieee_mapper_service_profile_entity_id, # 802.1p mapper Service Mapper Profile ID
+ interwork_tp_pointers=gem_entity_ids # Interworking TP IDs BP: oldvalue self.gemid
+ ).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-8021p-mapper-service-profile', status=status,
+ failed_attributes_mask=failed_attributes_mask,
+ unsupported_attributes_mask=unsupported_attributes_mask)
+
+ ################################################################################
+ # Unlock UNI
+ #
+ # EntityID will be referenced by:
+ # - MAC bridge port configuration data for the UNI side
+ # References:
+ # - Nothing
+
+ attributes = dict(
+ administrative_state=0 # 0 - Unlock
+ )
+ 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)
+
+ ################################################################################
+ # Create Extended VLAN Tagging Operation config
+ #
+ # 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=ethernet_uni_entity_id # Assoc ME, PPTP Entity Id
+ )
+
+ frame = ExtendedVlanTaggingOperationConfigurationDataFrame(
+ vlan_config_entity_id,
+ attributes=attributes
+ ).create()
+ results = yield omci.send(frame)
+
+ status = results.fields['omci_message'].fields['success_code']
+ error_mask = results.fields['omci_message'].fields['parameter_error_attributes_mask']
+ self.log.debug('create-extended-vlan-tagging-operation-configuration-data', status=status, error_mask=error_mask)
+
+ ################################################################################
+ # 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=0x8100, # input TPID
+ output_tpid=0x8100, # output TPID
+ downstream_mode=0, # inverse of upstream
+ )
+ frame = ExtendedVlanTaggingOperationConfigurationDataFrame(
+ vlan_config_entity_id,
+ attributes=attributes
+ ).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-extended-vlan-tagging-operation-configuration-data', status=status,
+ failed_attributes_mask=failed_attributes_mask,
+ unsupported_attributes_mask=unsupported_attributes_mask)
+
+ ################################################################################
+ # 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=cvid, # use this value as the VID in the inner VLAN tag
+ treatment_inner_tpid_de=4 # set TPID = 0x8100
+ )
+ )
+ frame = ExtendedVlanTaggingOperationConfigurationDataFrame(
+ vlan_config_entity_id, # Entity ID BP: Oldvalue 0x202
+ 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-extended-vlan-tagging-operation-configuration-data-untagged', status=status,
+ failed_attributes_mask=failed_attributes_mask,
+ unsupported_attributes_mask=unsupported_attributes_mask)
+
+ # 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']
+
+ ###############################################################################
+ # If here, we are done
+ self._bridge_initialized = True
+ device.reason = ''
self._handler.adapter_agent.update_device(device)
- # Try again later
- self._deferred = reactor.callLater(_STARTUP_RETRY_WAIT,
- self._initial_message_exchange)
+ except TimeoutError as e:
+ self.log.warn('rx-timeout', frame=frame)
+ self._deferred = reactor.callLater(_STARTUP_RETRY_WAIT,
+ self.resync_omci_settings)
+ returnValue('retry-pending')
- except Exception as e:
- self.log.exception('mib-upload', e=e)
- device.reason = 'MIB upload sequence failure: ' + e.message
- self._handler.adapter_agent.update_device(device)
+ except Exception as e:
+ self.log.exception('omci-setup', e=e)
+ device.reason = 'OMCI setup sequence failure: ' + e.message
+ self._handler.adapter_agent.update_device(device)
- # Try again later
- self._deferred = reactor.callLater(_STARTUP_RETRY_WAIT,
- self._initial_message_exchange)
-
- @inlineCallbacks
- def _perform_mib_download(self):
- """
- Called after basic OMCI Synchronization (MIB upload). Begin to set up
- some basic OMCI settings common for most expected configurations
-
- Upon successful completion, any xPON information received so far will be
- acted upon.
-
- On failure, restart the initial message exchange
- """
- self.log.info('mib-download-start')
- self._deferred = None
-
- if self._handler.device_id is None or not self.enabled:
- returnValue('not-enabled')
-
- omci = self._handler.omci
-
- if self._handler.is_mock:
- self._bridge_initialized = True
- self._deferred = reactor.callLater(0, self._sync_existing_xpon)
- returnValue('is-mock')
-
- # reset incoming message queue
- omci.flush()
- device = self._handler.adapter_agent.get_device(self._handler.device_id)
-
- device.reason = 'Performing MIB Download'
- self._handler.adapter_agent.update_device(device)
-
- if not self.enabled or device is None:
- returnValue('not-enabled')
-
- #############################################
- # All our variables here
- # TODO: Move elsewhere in future version of this software
- frame = None
- gal_enet_profile_entity_id = 0x100 # Any Unique Entity ID BP: old value 1
- ieee_mapper_service_profile_entity_id = 0x100 # Entity ID BP: old value 0x8001
- mac_bridge_service_profile_entity_id = 0x100 # Entity ID BP: old value 0x201
- mac_bridge_port_ani_entity_id = 0x100 # BP: oldvalue 0x201
- ethernet_uni_entity_id = 0x101
- vlan_tcis_1 = 0x900
- cvid = 2 # TODO: Get from xPON and/or device adapter
- tcont_entity_id = 0x100 # Entity ID, ONT is set to 0x100
- tcont_alloc_id = 0x400 # Alloc ID, 1024 - Tcont
- gem_entity_id = 0x4900 # Entity ID, unique Id
- gem_port_id = 0x400 # Port ID, 2304 - Gem Id
- gem_interworking_entity_id = 0x4900
- vlan_config_entity_id = vlan_tcis_1 # Entity ID BP: Oldvalue 0x202
-
- try:
- ################################################################################
- #
- #
- # EntityID will be referenced by:
- # -
- # -
- # -
- # -
- # References:
- # -
- # -
-
- frame = TcontFrame(tcont_entity_id, tcont_alloc_id).set()
- results = yield omci.send(frame)
- # results = yield send_set_tcont(omci, 0x100, # Entity ID, ONT is set to 0x100
- # 0x400) # Alloc ID, 1024 - Tcont
-
- 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-tcont', status=status,
- failed_attributes_mask=failed_attributes_mask,
- unsupported_attributes_mask=unsupported_attributes_mask)
-
- ################################################################################
- direction = "bi-directional"
-
- # TODO: For TM, is this the entity ID for a traffic descriptor?
- frame = GemPortNetworkCtpFrame(
- gem_entity_id,
- port_id=gem_port_id, # Port ID, 2304 - Gem ID
- tcont_id=tcont_entity_id, # TCONT Entity ID, as set in TCONT set
- direction=direction, # Direction, bidirectional
- upstream_tm=0x8000 # TM ID, 32768 unique ID set in TD set TODO: Parameterize
- ).create()
- results = yield omci.send(frame)
- # results = yield send_create_gem_port_network_ctp(omci, 0x4900, # Entity ID, unique Id
- # 0x400, # Port ID, 2304 - Gem Id
- # 0x100, # TCONT Entity ID, as set in TCONT set
- # direction, # Direction, bidirectional
- # 0x8000) # TM ID, 32768 unique Id set in TD set
-
- status = results.fields['omci_message'].fields['success_code']
- error_mask = results.fields['omci_message'].fields['parameter_error_attributes_mask']
- self.log.debug('create-gem-port-network-ctp', status=status, error_mask=error_mask)
-
- ################################################################################
- # GEM Interworking config
- #
- #
- # EntityID will be referenced by:
- # -
- # -
- # -
- # -
- # References:
- # -
- # -
- # TODO: for the service_profile_pointer=0x100, is this create/set somewhere later
-
- frame = GemInterworkingTpFrame(
- gem_interworking_entity_id,
- gem_port_network_ctp_pointer=gem_entity_id, # GEMPort NET CTP ID, as set in CTP create
- interworking_option=5, # IEEE 802.1
- service_profile_pointer=ieee_mapper_service_profile_entity_id,
- interworking_tp_pointer=0x0,
- pptp_counter=1,
- gal_profile_pointer=0 # TODO: make? -> gal_enet_profile_entity_id # BP: HACK old value 0x1 (TODO: Balaji had this set to 0 in his test sequence)
- ).create()
- results = yield omci.send(frame)
- # results = yield send_create_gem_inteworking_tp(omci, 0x4900, # any Unique Entity ID
- # 0x4900, # GEMPort NET CTP ID, as set in CTP create
- # 0x100) # 802.1p mapper Service Mapper Profile ID
-
- status = results.fields['omci_message'].fields['success_code']
- error_mask = results.fields['omci_message'].fields['parameter_error_attributes_mask']
- self.log.debug('create-gem-interworking-tp', status=status, error_mask=error_mask)
-
- ########################################################################################
- # Create GalEthernetProfile - Once per ONU/PON interface
- #
- # EntityID will be referenced by:
- # - GEM Interworking TPs when a new GEM Port is created
- # References:
- # - Nothing
-
- frame = GalEthernetProfileFrame(gal_enet_profile_entity_id,
- max_gem_payload_size=1518).create() # Max GEM Payload size
- results = yield omci.send(frame)
- # results = yield send_create_gal_ethernet_profile(omci,
- # 0x100, # Any Unique Entity ID BP: old value 1
- # 1518) # Max GEM Payload size
-
- status = results.fields['omci_message'].fields['success_code']
- error_mask = results.fields['omci_message'].fields['parameter_error_attributes_mask']
- self.log.debug('create-gal-ethernet-profile', status=status, error_mask=error_mask)
-
- ################################################################################
- # MAC Bridge Service Profile - Once per UNI
- #
- # EntityID will be referenced by:
- # - MAC Bridge Port Configuration Data
- # References:
- # - Nothing
-
- attributes = {
- 'spanning_tree_ind': False,
- # TODO: CB: see if we need or can use any of the following...
- # 'learning_ind': True,
- # 'priority': 0x8000,
- # 'max_age': 20 * 256,
- # 'hello_time': 2 * 256,
- # 'forward_delay': 15 * 256,
- # 'unknown_mac_address_discard': True
- }
- frame = MacBridgeServiceProfileFrame(mac_bridge_service_profile_entity_id,
- attributes).create()
- results = yield omci.send(frame)
- # results = yield send_create_mac_bridge_service_profile(omci, 0x100) # Entity ID BP: old value 0x201
-
- status = results.fields['omci_message'].fields['success_code']
- error_mask = results.fields['omci_message'].fields['parameter_error_attributes_mask']
- self.log.debug('create-mac-bridge-service-profile', status=status, error_mask=error_mask)
-
- ################################################################################
- # IEEE 802.1 Mapper Service config - Once per PON
- #
- # EntityID will be referenced by:
- # - MAC Bridge Port Configuration Data for the PON port
- # References:
- # - Nothing at this point. When a GEM port is created, this entity will
- # be updated to reference the GEM Interworking TP
-
- frame = Ieee8021pMapperServiceProfileFrame(ieee_mapper_service_profile_entity_id).create()
- results = yield omci.send(frame)
- # results = yield send_create_8021p_mapper_service_profile(omci, 0x100) # Entity ID BP: old value 0x8001
-
- status = results.fields['omci_message'].fields['success_code']
- error_mask = results.fields['omci_message'].fields['parameter_error_attributes_mask']
- self.log.debug('create-8021p-mapper-service-profile', status=status, error_mask=error_mask)
-
- ################################################################################
- # Create MAC Bridge Port Configuration Data for the PON port via IEEE 802.1
- # mapper service. Upon receipt by the ONU, the ONU will create an instance
- # of the following before returning the response.
- #
- # - MAC bridge port designation data
- # - MAC bridge port filter table data
- # - MAC bridge port bridge table data
- #
- # EntityID will be referenced by:
- # - Implicitly by the VLAN tagging filter data
- # -
- # -
- # -
- # References:
- # - MAC Bridge Service Profile (the bridge)
- # - IEEE 802.1p mapper service profile for PON port
-
- frame = MacBridgePortConfigurationDataFrame(
- mac_bridge_port_ani_entity_id, # Entity ID
- bridge_id_pointer=mac_bridge_service_profile_entity_id, # Bridge Entity ID BP: oldvalue 0x201
- # TODO: The PORT number for this port and the UNI port are the same. Is this correct?
- port_num=0, # Port ID BP: oldvalue 2
- tp_type=3, # TP Type (IEEE 802.1p mapper service) BP: oldvalue 1, 802.1 mapper GPON intf
- tp_pointer=ieee_mapper_service_profile_entity_id # TP ID, 8021p mapper ID BP: oldvalue 0x102
- ).create()
- results = yield omci.send(frame)
- # results = yield send_create_mac_bridge_port_configuration_data(omci,
- # 0x100, # Entity ID BP: oldvalue 0x201
- # 0x100, # Bridge Entity ID BP: oldvalue 0x201
- # 0, # Port ID BP: oldvalue 2
- # 3, # TP Type BP: oldvalue 1, 802.1 mapper GPON interface
- # 0x100) # TP ID, 8021p mapper Id BP: oldvalue 0x102
-
- status = results.fields['omci_message'].fields['success_code']
- error_mask = results.fields['omci_message'].fields['parameter_error_attributes_mask']
- self.log.debug('create-mac-bridge-port-configuration-data-part-1', status=status, error_mask=error_mask)
-
- ################################################################################
- # MAC Bridge Port config
- # This configuration is for Ethernet UNI
- #
- # EntityID will be referenced by:
- # -
- # -
- # -
- # -
- # References:
- # - MAC Bridge Service Profile (the bridge)
- # - PPTP Ethernet UNI
-
- frame = MacBridgePortConfigurationDataFrame(
- 0x000, # Entity ID BP: oldvalue 0x201
- bridge_id_pointer=mac_bridge_service_profile_entity_id, # Bridge Entity ID BP: oldvalue 0x201
- port_num=0, # Port ID BP: oldvalue 3
- tp_type=1, # PPTP Ethernet UNI BP: oldvalue 3
- tp_pointer=ethernet_uni_entity_id # TP ID, 8021p mapper Id BP: oldvalue 0x8001
- ).create()
- results = yield omci.send(frame)
- # results = yield send_create_mac_bridge_port_configuration_data(omci,
- # 0x000, # Entity ID BP: oldvalue 0x2102
- # 0x100, # Bridge Entity ID BP: oldvalue 0x201
- # 0, # Port ID BP: oldvalue 3
- # 1, # TP Type, Ethernet UNI BP: oldvalue 3
- # 0x101) # TP ID, PPTP UNI Entity Id BP: oldvalue 0x8001
-
- status = results.fields['omci_message'].fields['success_code']
- error_mask = results.fields['omci_message'].fields['parameter_error_attributes_mask']
- self.log.debug('create-mac-bridge-port-configuration-data-part-2', status=status, error_mask=error_mask)
-
- ################################################################################
- # VLAN Tagging Filter config
- #
- # EntityID will be referenced by:
- # - Nothing
- # References:
- # - Implicitly linked to an instance of the MAC bridge port configuration data
- # for the PON port
- # TODO: Probably need to get VLAN ID from device.vlan
- # Set anything, this request will not be used when using Extended Vlan
-
- frame = VlanTaggingFilterDataFrame(
- mac_bridge_port_ani_entity_id, # Entity ID BP: Oldvalue 0x2102
- vlan_tcis=[vlan_tcis_1], # VLAN IDs BP: cvid
- forward_operation=0x10
- ).create()
- results = yield omci.send(frame)
- # results = yield send_create_vlan_tagging_filter_data(omci, 0x100, # Entity ID BP: Oldvalue 0x2102
- # 0x900) # VLAN ID BP: cvid
-
- status = results.fields['omci_message'].fields['success_code']
- error_mask = results.fields['omci_message'].fields['parameter_error_attributes_mask']
- self.log.debug('create-vlan-tagging-filter-data', status=status, error_mask=error_mask)
-
- ################################################################################
- # Update the IEEE 802.1p Mapper Service Profile config
- #
- # EntityID was created prior to this call
- # References:
- # -
- # -
- # TODO: All p-bits currently go to the one and only GEMPORT ID for now
-
- frame = Ieee8021pMapperServiceProfileFrame(
- ieee_mapper_service_profile_entity_id, # 802.1p mapper Service Mapper Profile ID
- interwork_tp_pointers=[gem_entity_id] # Interworking TP IDs BP: oldvalue self.gemid
- ).set()
- results = yield omci.send(frame)
- # results = yield send_set_8021p_mapper_service_profile(omci, 0x100, 0x4900)
-
- 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-8021p-mapper-service-profile', status=status,
- failed_attributes_mask=failed_attributes_mask,
- unsupported_attributes_mask=unsupported_attributes_mask)
-
- ################################################################################
- # Unlock UNI
- #
- # EntityID will be referenced by:
- # - MAC bridge port configuration data for the UNI side
- # References:
- # - Nothing
-
- attributes = dict(
- administrative_state=0 # 0 - Unlock
- )
- frame = PptpEthernetUniFrame(
- ethernet_uni_entity_id, # Entity ID
- attributes=attributes # See above
- ).set()
- results = yield omci.send(frame)
- #results = yield send_set_pptp_ethernet_uni(omci, 0x101) # Entity ID
-
- 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)
-
- ################################################################################
- # Create Extended VLAN Tagging Operation config
- #
- # 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 BP: Oldvalue 2
- associated_me_pointer=ethernet_uni_entity_id # Assoc ME, PPTP Entity Id BP: Oldvalue 0x102
- )
-
- frame = ExtendedVlanTaggingOperationConfigurationDataFrame(
- vlan_config_entity_id,
- attributes=attributes # See above
- ).create()
- results = yield omci.send(frame)
- # results = yield send_create_extended_vlan_tagging_operation_configuration_data(omci,
- # 0x900, # Entity ID BP: Oldvalue 0x202
- # 2, # Assoc Type, PPTP Ethernet UNI BP: Oldvalue 2
- # 0x101) # Assoc ME, PPTP Entity Id BP: Oldvalue 0x102
-
- status = results.fields['omci_message'].fields['success_code']
- error_mask = results.fields['omci_message'].fields['parameter_error_attributes_mask']
- self.log.debug('create-extended-vlan-tagging-operation-configuration-data', status=status, error_mask=error_mask)
-
- ################################################################################
- # 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=0x8100, # input TPID
- output_tpid=0x8100, # output TPID
- downstream_mode=0, # inverse of upstream
- )
- frame = ExtendedVlanTaggingOperationConfigurationDataFrame(
- vlan_config_entity_id, # Entity ID BP: Oldvalue 0x202
- attributes=attributes # See above
- ).set()
- results = yield omci.send(frame)
- # results = yield send_set_extended_vlan_tagging_operation_tpid_configuration_data(omci,
- # 0x900, # Entity ID BP: Oldvalue 0x202
- # 0x8100, # input TPID
- # 0x8100) # output TPID
-
- 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-extended-vlan-tagging-operation-configuration-data', status=status,
- failed_attributes_mask=failed_attributes_mask,
- unsupported_attributes_mask=unsupported_attributes_mask)
-
- ################################################################################
- # 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=cvid, # use this value as the VID in the inner VLAN tag
- treatment_inner_tpid_de=4 # set TPID = 0x8100
- )
- )
- frame = ExtendedVlanTaggingOperationConfigurationDataFrame(
- vlan_config_entity_id, # Entity ID BP: Oldvalue 0x202
- attributes=attributes # See above
- ).set()
- results = yield omci.send(frame)
- # results = yield send_set_extended_vlan_tagging_operation_vlan_configuration_data_untagged(omci, 0x900,
- # 0x1000,
- # 2)
- 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-extended-vlan-tagging-operation-configuration-data-untagged', status=status,
- failed_attributes_mask=failed_attributes_mask,
- unsupported_attributes_mask=unsupported_attributes_mask)
-
- # 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']
-
- ###############################################################################
- # If here, we are done
- device.reason = ''
- self._handler.adapter_agent.update_device(device)
-
- ######################################################################
- # If here, we can add TCONTs/GEM Ports/... as needed
-
- self._bridge_initialized = True
- self._deferred = reactor.callLater(0, self._sync_existing_xpon)
-
- except TimeoutError as e:
- self.log.warn('rx-timeout', frame=frame)
- # Try again later. May not have been discovered
- self._deferred = reactor.callLater(_STARTUP_RETRY_WAIT,
- self._initial_message_exchange)
- returnValue('retry-pending')
-
- except Exception as e:
- self.log.exception('mib-download', e=e)
- device.reason = 'MIB download sequence failure: ' + e.message
- self._handler.adapter_agent.update_device(device)
-
- # Try again later
- self._deferred = reactor.callLater(_STARTUP_RETRY_WAIT,
- self._initial_message_exchange)
-
- @inlineCallbacks
- def _sync_existing_xpon(self):
- """
- Run through existing TCONT and GEM Ports and push into hardware
- """
- # for tcont in self._tconts.itervalues():
- # try:
- # yield self.add_tcont(tcont, reflow=True)
- # except Exception as e:
- # self.log.exception('tcont-reflow', e=e, tcont=tcont)
- #
- # for gem_port in self._gem_ports.itervalues():
- # try:
- # yield self.add_gem_port(gem_port, reflow=True)
- #
- # except Exception as e:
- # self.log.exception('gem-port-reflow', e=e, gem_port=gem_port)
-
- returnValue('Done')
+ # Try again later
+ self._deferred = reactor.callLater(_STARTUP_RETRY_WAIT,
+ self.resync_omci_settings)
- @inlineCallbacks
def add_tcont(self, tcont, reflow=False):
"""
Creates/ a T-CONT with the given alloc-id
@@ -938,48 +659,37 @@
:return: (deferred)
"""
if not self._valid:
- returnValue('Deleting')
+ return # Deleting
if not reflow and tcont.alloc_id in self._tconts:
- returnValue('already created')
+ return # already created
self.log.info('add', tcont=tcont, reflow=reflow)
self._tconts[tcont.alloc_id] = tcont
- if not self.bridge_initialized:
- returnValue('Bridge Not Initialized')
+ # only start setting sequence if there are at least one gem add
+ if len(self._gem_ports) > 0 and self._bridge_initialized:
+ self._cancel_resync_deferred()
+ self._bridge_initialized = False
+ self._resync_deferred = reactor.callLater(0, self.resync_omci_settings)
- try:
- results = yield tcont.add_to_hardware(self._handler.omci)
-
- except Exception as e:
- self.log.exception('tcont', tcont=tcont, reflow=reflow, e=e)
- # May occur with xPON provisioning, use hw-resync to recover
- results = 'resync needed'
-
- returnValue(results)
-
- @inlineCallbacks
def update_tcont_td(self, alloc_id, new_td):
tcont = self._tconts.get(alloc_id)
if tcont is None:
- returnValue('not-found')
+ return # not-found
tcont.traffic_descriptor = new_td
- if not self.bridge_initialized:
- returnValue('Bridge Not Initialized')
-
- try:
- results = yield tcont.add_to_hardware(self._handler.omci)
-
- except Exception as e:
- self.log.exception('tcont', tcont=tcont, e=e)
- # May occur with xPON provisioning, use hw-resync to recover
- results = 'resync needed'
-
- returnValue(results)
+ # TODO: Not yet implemented
+ #TODO: How does this affect ONU tcont settings?
+ #try:
+ # results = yield tcont.add_to_hardware(self._handler.omci)
+ #except Exception as e:
+ # self.log.exception('tcont', tcont=tcont, e=e)
+ # # May occur with xPON provisioning, use hw-resync to recover
+ # results = 'resync needed'
+ # returnValue(results)
@inlineCallbacks
def remove_tcont(self, alloc_id):
@@ -988,20 +698,15 @@
if tcont is None:
returnValue('nop')
- del self._tconts[alloc_id]
-
- if not self.bridge_initialized:
- returnValue('Bridge Not Initialized')
-
try:
+ del self._tconts[alloc_id]
+
results = yield tcont.remove_from_hardware(self._handler.omci)
+ returnValue(results)
except Exception as e:
self.log.exception('delete', e=e)
- results = e
- # raise
-
- returnValue(results)
+ raise
def gem_port(self, gem_id):
return self._gem_ports.get(gem_id)
@@ -1011,7 +716,6 @@
"""Get all GEM Port IDs used by this ONU"""
return sorted([gem_id for gem_id, gem in self._gem_ports.items()])
- @inlineCallbacks
def add_gem_port(self, gem_port, reflow=False):
"""
Add a GEM Port to this ONU
@@ -1021,47 +725,111 @@
:return: (deferred)
"""
if not self._valid:
- returnValue('Deleting')
+ return # Deleting
if not reflow and gem_port.gem_id in self._gem_ports:
- returnValue('nop')
+ return # nop
self.log.info('add', gem_port=gem_port, reflow=reflow)
self._gem_ports[gem_port.gem_id] = gem_port
- if not self.bridge_initialized:
- returnValue('Bridge Not Initialized')
-
- try:
- results = yield gem_port.add_to_hardware(self._handler.omci)
- # TODO: Are flows affected by this change?
-
- except Exception as e:
- self.log.exception('gem-port', gem_port=gem_port, reflow=reflow, e=e)
- # This can happen with xPON if the ONU has been provisioned, but the PON Discovery
- # has not occurred for the ONU. Rely on hw sync to recover
- results = 'resync needed'
-
- returnValue(results)
+ # assuming tcont was already added to start start settings sequence
+ if self._bridge_initialized:
+ self._cancel_resync_deferred()
+ self._bridge_initialized = False
+ self._resync_deferred = reactor.callLater(0, self.resync_omci_settings)
@inlineCallbacks
def remove_gem_id(self, gem_id):
+ """
+ Remove a GEM Port from this ONU
+
+ :param gem_port: (GemPort) GEM Port to remove
+ :return: deferred
+ """
gem_port = self._gem_ports.get(gem_id)
if gem_port is None:
returnValue('nop')
- del self._gem_ports[gem_id]
-
- if not self.bridge_initialized:
- returnValue('Bridge Not Initialized')
-
try:
+ del self._gem_ports[gem_id]
+
results = yield gem_port.remove_from_hardware(self._handler.omci)
- # TODO: Are flows affected by this change?
+ returnValue(results)
except Exception as ex:
self.log.exception('gem-port-delete', e=ex)
raise
- returnValue(results)
+ def _subscribe_to_events(self):
+ from voltha.extensions.omci.onu_device_entry import OnuDeviceEvents, \
+ OnuDeviceEntry, IN_SYNC_KEY
+ from voltha.extensions.omci.omci_cc import OMCI_CC, OmciCCRxEvents, \
+ CONNECTED_KEY
+
+ def in_sync_handler(_topic, msg):
+ if self._in_sync_subscription is not None:
+ try:
+ in_sync = msg[IN_SYNC_KEY]
+
+ if in_sync:
+ # Only call this once as well
+ bus = self._onu_omci_device.event_bus
+ bus.unsubscribe(self._in_sync_subscription)
+ self._in_sync_subscription = None
+
+ # Start up device_info load
+ reactor.callLater(0, self._mib_in_sync)
+
+ except Exception as e:
+ self.log.exception('in-sync', e=e)
+
+ def onu_is_reachable(_topic, msg):
+ """
+ Reach-ability change event
+ :param msg: (dict) 'connected' key holds True if reachable
+ """
+ if self._connectivity_subscription is not None:
+ try:
+ connected = msg[CONNECTED_KEY]
+
+ # TODO: For now, only care about the first.
+ # Later we could use this for a heartbeat, but may want some hysteresis
+ # Cancel any 'reachable' subscriptions
+ if connected:
+ evt_bus = self._onu_omci_device.omci_cc.event_bus
+ evt_bus.unsubscribe(self._connectivity_subscription)
+ self._connectivity_subscription = None
+ self._connected = True
+
+ device = self._handler.adapter_agent.get_device(self._handler.device_id)
+ device.connect_status = ConnectStatus.REACHABLE
+ self._handler.adapter_agent.update_device(device)
+
+ except Exception as e:
+ self.log.exception('onu-reachable', e=e)
+
+ # OMCI MIB Database sync status
+ bus = self._onu_omci_device.event_bus
+ topic = OnuDeviceEntry.event_bus_topic(self._handler.device_id,
+ OnuDeviceEvents.MibDatabaseSyncEvent)
+ self._in_sync_subscription = bus.subscribe(topic, in_sync_handler)
+
+ # OMCI-CC Connectivity Events (for reachbility/heartbeat)
+ bus = self._onu_omci_device.omci_cc.event_bus
+ topic = OMCI_CC.event_bus_topic(self._handler.device_id,
+ OmciCCRxEvents.Connectivity)
+ self._connectivity_subscription = bus.subscribe(topic, onu_is_reachable)
+
+ def _unsubscribe_to_events(self):
+ if self._in_sync_subscription is not None:
+ bus = self._onu_omci_device.event_bus
+ bus.unsubscribe(self._in_sync_subscription)
+ self._in_sync_subscription = None
+
+ if self._connectivity_subscription is not None:
+ bus = self._onu_omci_device.omci_cc.event_bus
+ bus.unsubscribe(self._connectivity_subscription)
+ self._connectivity_subscription = None
+
diff --git a/voltha/adapters/adtran_onu/uni_port.py b/voltha/adapters/adtran_onu/uni_port.py
index 43ccfee..6fc7b04 100644
--- a/voltha/adapters/adtran_onu/uni_port.py
+++ b/voltha/adapters/adtran_onu/uni_port.py
@@ -24,8 +24,10 @@
class UniPort(object):
"""Wraps southbound-port(s) support for ONU"""
+ DEFAULT_UNTAGGED_VLAN = 4092
- def __init__(self, handler, name, port_no, control_vlan=None):
+ def __init__(self, handler, name, port_no, ofp_port_no, subscriber_vlan=None,
+ untagged_vlan=None):
self.log = structlog.get_logger(device_id=handler.device_id,
port_no=port_no)
self._enabled = False
@@ -33,21 +35,22 @@
self._name = name
self._port = None
self._port_number = port_no
- self._logical_port_number = None
- self._control_vlan = control_vlan
+ self._ofp_port_no = ofp_port_no # Set at by creator (vENET create)
+ self._logical_port_number = None # Set at time of logical port creation
+ self._subscriber_vlan = subscriber_vlan
+ self._untagged_vlan = untagged_vlan
self._admin_state = AdminState.ENABLED
self._oper_status = OperStatus.ACTIVE
# TODO Add state, stats, alarm reference, ...
-
pass
def __str__(self):
return "UniPort: {}:{}".format(self.name, self.port_number)
@staticmethod
- def create(handler, name, port_no, control_vlan):
- port = UniPort(handler, name, port_no, control_vlan)
+ def create(handler, name, port_no, ofp_port_no, subscriber_vlan, untagged_vlan):
+ port = UniPort(handler, name, port_no, ofp_port_no,subscriber_vlan, untagged_vlan)
return port
def _start(self):
@@ -117,20 +120,37 @@
pass
@staticmethod
- def decode_openflow_port_and_control_vlan(self, venet_info):
+ def decode_venet(venet_info):
try:
- # Allow spaces or dashes as separator, select last as
- # the port number
-
+ # Allow spaces or dashes as separator, select last as the
+ # port number. UNI-1, UNI 1, and UNI 3-2-1 are the same
port_no = int(venet_info['name'].replace(' ', '-').split('-')[-1:][0])
- cntl_vlan = port_no
+ subscriber_vlan = port_no
+ untagged_vlan = UniPort.DEFAULT_UNTAGGED_VLAN
+ try:
+ # Subscriber VLAN and Untagged vlan are comma separated
+ parts = venet_info['description'].split(',')
+ sub_part = next((part for part in parts if 'vlan' in part.lower()), None)
+ untagged_part = next((part for part in parts if 'untagged' in part.lower()), None)
+ try:
+ if sub_part is not None:
+ subscriber_vlan = int(sub_part.split(':')[-1:][0])
+ except Exception as e:
+ pass
+ try:
+ if untagged_part is not None:
+ untagged_vlan = int(untagged_part.split(':')[-1:][0])
+ except Exception as e:
+ pass
+ except Exception as e:
+ pass
- return port_no, cntl_vlan
+ return port_no, subscriber_vlan, untagged_vlan
except ValueError:
- self.log.error('invalid-uni-port-name', name=venet_info['name'])
+ pass
except KeyError:
- self.log.error('invalid-venet-data', data=venet_info)
+ pass
def get_port(self):
"""
@@ -145,28 +165,32 @@
oper_status=self._oper_status)
return self._port
- def add_logical_port(self, openflow_port_no, control_vlan=None,
+ def add_logical_port(self, openflow_port_no, subscriber_vlan=None,
capabilities=OFPPF_10GB_FD | OFPPF_FIBER,
speed=OFPPF_10GB_FD):
- if self._logical_port_number is None:
- self._logical_port_number = openflow_port_no
- self._control_vlan = control_vlan
+ # Use vENET provisioned values if none supplied
+ port_no = openflow_port_no or self._ofp_port_no
+ vlan = subscriber_vlan or self._subscriber_vlan
+
+ if self._logical_port_number is None and port_no is not None:
+ self._logical_port_number = port_no
+ self._subscriber_vlan = vlan
device = self._handler.adapter_agent.get_device(self._handler.device_id)
- if control_vlan is not None and device.vlan != control_vlan:
- device.vlan = control_vlan
+ if vlan is not None and device.vlan != vlan:
+ device.vlan = vlan
self._handler.adapter_agent.update_device(device)
openflow_port = ofp_port(
- port_no=openflow_port_no,
+ port_no=port_no,
hw_addr=mac_str_to_tuple('08:00:%02x:%02x:%02x:%02x' %
((device.parent_port_no >> 8 & 0xff),
device.parent_port_no & 0xff,
- (openflow_port_no >> 8) & 0xff,
- openflow_port_no & 0xff)),
- name='uni-{}'.format(openflow_port_no),
+ (port_no >> 8) & 0xff,
+ port_no & 0xff)),
+ name='uni-{}'.format(port_no),
config=0,
state=OFPPS_LIVE,
curr=capabilities,
@@ -177,7 +201,7 @@
)
self._handler.adapter_agent.add_logical_port(self._handler.logical_device_id,
LogicalPort(
- id='uni-{}'.format(openflow_port),
+ id='uni-{}'.format(port_no),
ofp_port=openflow_port,
device_id=device.id,
device_port_no=self._port_number))