VOL-598: Updates to Adtran ONU OMCI library
Change-Id: Ibb494bb79fd60ee728e4f81408dc375a45eaee62
diff --git a/voltha/adapters/adtran_onu/adtran_onu.py b/voltha/adapters/adtran_onu/adtran_onu.py
index c1ff799..c0d9ae9 100755
--- a/voltha/adapters/adtran_onu/adtran_onu.py
+++ b/voltha/adapters/adtran_onu/adtran_onu.py
@@ -36,7 +36,7 @@
device_handler_class=AdtranOnuHandler,
name='adtran_onu',
vendor='Adtran, Inc.',
- version='0.3',
+ version='0.4',
device_type='adtran_onu',
vendor_id='ADTN')
diff --git a/voltha/adapters/adtran_onu/adtran_onu_handler.py b/voltha/adapters/adtran_onu/adtran_onu_handler.py
index 8f8f75d..2231dd4 100644
--- a/voltha/adapters/adtran_onu/adtran_onu_handler.py
+++ b/voltha/adapters/adtran_onu/adtran_onu_handler.py
@@ -17,7 +17,8 @@
import arrow
from voltha.adapters.adtran_olt.xpon.adtran_xpon import AdtranXPON
-from omci.omci_cc import OMCISupport
+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
@@ -119,7 +120,7 @@
@property
def is_mock(self):
- return self._is_mock # Not pointing to any real hardware
+ return self._is_mock # Not pointing to real hardware
@property
def olt_created(self):
@@ -166,7 +167,9 @@
#
self._cancel_deferred()
- self._omci = OMCISupport(self, self.adapter, self.device_id)
+ 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
@@ -241,6 +244,7 @@
device.model = 'n/a'
device.hardware_version = 'n/a'
device.firmware_version = 'n/a'
+ device.reason = ''
# TODO: Support more versions as needed
images = Image(version='NOT AVAILABLE')
@@ -287,14 +291,13 @@
self.adapter_agent.add_port(device.id, uni_port.get_port())
device.serial_number = uuid4().hex
- # self._add_logical_port(device.vlan, control_vlan=device.vlan)
uni_port.add_logical_port(device.vlan, control_vlan=device.vlan)
# Start things up for this ONU Handler.
self.enabled = True
# Start collecting stats from the device after a brief pause
- reactor.callLater(10, self.start_kpi_collection, device.id)
+ reactor.callLater(30, self.start_kpi_collection, device.id)
self.adapter_agent.update_device(device)
@@ -325,6 +328,7 @@
device = self.adapter_agent.get_device(device.id)
device.connect_status = ConnectStatus.REACHABLE
device.oper_status = OperStatus.ACTIVE
+ device.reason = ''
self.adapter_agent.update_device(device)
self.log.info('reconciling-ONU-device-ends')
@@ -554,6 +558,7 @@
previous_conn_status = device.connect_status
device.oper_status = OperStatus.ACTIVATING
device.connect_status = ConnectStatus.UNREACHABLE
+ device.reason = 'Rebooting'
self.adapter_agent.update_device(device)
@@ -571,6 +576,7 @@
device = self.adapter_agent.get_device(self.device_id)
device.oper_status = previous_oper_status
device.connect_status = previous_conn_status
+ device.reason = ''
self.adapter_agent.update_device(device)
self.log.info('rebooted', device_id=self.device_id)
@@ -598,6 +604,7 @@
# Update the device operational status to UNKNOWN
device.oper_status = OperStatus.UNKNOWN
device.connect_status = ConnectStatus.UNREACHABLE
+ device.reason = 'Disabled'
self.adapter_agent.update_device(device)
# Remove the uni logical port from the OLT, if still present
@@ -681,6 +688,7 @@
device = self.adapter_agent.get_device(device.id)
device.oper_status = OperStatus.ACTIVE
+ device.reason = ''
self.enabled = True
self.adapter_agent.update_device(device)
@@ -908,7 +916,8 @@
td = self.traffic_descriptors.get(tcont.get('td-ref'))
traffic_descriptor = td['object'] if td is not None else None
- tcont['object'] = OnuTCont.create(self, tcont, traffic_descriptor)
+ 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]
@@ -998,7 +1007,7 @@
def on_gemport_create(self, gem_port):
from onu_gem_port import OnuGemPort
- gem_port['object'] = OnuGemPort.create(self, gem_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:
diff --git a/voltha/adapters/adtran_onu/heartbeat.py b/voltha/adapters/adtran_onu/heartbeat.py
index 02fec22..4e54459 100644
--- a/voltha/adapters/adtran_onu/heartbeat.py
+++ b/voltha/adapters/adtran_onu/heartbeat.py
@@ -61,10 +61,10 @@
if self._enabled != value:
self._enabled = value
- if value:
- self._start()
- else:
- self._stop()
+ # if value:
+ # self._start()
+ # else:
+ # self._stop()
@property
def check_item(self):
diff --git a/voltha/adapters/adtran_onu/omci/README.md b/voltha/adapters/adtran_onu/omci/README.md
index 2d030fe..babd884 100644
--- a/voltha/adapters/adtran_onu/omci/README.md
+++ b/voltha/adapters/adtran_onu/omci/README.md
@@ -1,12 +1,97 @@
#OMCI Support
This directory contains classes to assist in the creation, transmission,
-and reception of OMCI frames on this ONU Adapter.
+and reception of OMCI frames on this ONU Adapter. A number of these files (but
+not all) could be moved into the common *.../voltha/extensions/omci* subdirectory.
##Files
+### omci_cc.py
-*TODO*: Add info on each file and what it is for
+The *omci_cc.py* file contains the OMCI communications channel for sending and receiving
+OMCI messages. For transmits, it will send the OMCI message to the proper proxy channel,
+but for received, OMCI frames, your device adapter will need to call the
+*receive_message()* method.
+
+The communications channel will return a deferred on a Tx request which will fire when
+a corresponding response is received or if a timeout or other error occurs. When a
+successful response is received, the *OMCI_CC* will also look at *Get, Set, Create*, and
+*Delete* messages prior to calling any additional callbacks so that the MIB Database can be
+checked or updated as needed. Note that the MIB Database is not yet implemented.
+
+ONU Autonomous messages are also handled (Test Results messages are TBD) and these are
+placed
+
+A collection of statistics are available in case the ONU wishes to publish a
+PM group containing these statistics. The Adtran ONU does so in the *onu_pm_metrics.py*
+file.
+
+Finally, a list of vendor-specific ME's can be passed to the class initializer so that
+they are registered in the class_id map. This allows for successful decode of custom MEs.
+See the Adtran ONU's instantiation of the *OMCI_CC* class as an example of how to
+add vendor-specific MEs.
+
+### me_frame.py
+
+This file contains the base class implementation that helps to transform defined
+Managed EntityClasses into OMCI Frames with specific actions (*get, set, create,
+delete, ...*). Prior this class, frames to do each action were hand created methods.
+
+Besides providing methods for creating OMCI frames, the ME attributes names, access
+attributes, and allowed operations are checked to verify that the action is valid
+for both the ME as well as the specific attribute.
+
+What is currently missing is other OMCI actions have not been coded:
+ - GetNext
+ - GetCurrentData
+ - GetAllAlarms
+ - GetAllAlarmsNext
+ - MibUpload
+ - MibUploadNext
+ - MibReset
+ - Test
+ - StartSoftwareDownload
+ - DownloadSection
+ - EndSoftwareDownload
+ - ActivateSoftware
+ - CommitSoftware
+ - SynchronizeTime
+ - Reboot
+
+For many of these actions, such as MibReset, these are only performed on a specific
+ME and it may be best to provide these as explicit static methods.
+
+### omci_me.py
+
+This file is a collection of ME classes derived from the MEFrame base class. For many
+of the currently defined ME's in *omci_entities.py*
+
+### omci_defs.py
+
+This file contains an additional status code enumeration which could be merged with
+the main OMCI extensions directory.
+
+### omci_entities.py
+
+This is an Adtran ONU specific file to add custom OMCI **OMCI_CC** entities and a function
+that can be called by the **OMCI_CC** class to install them into the appropriate locations
+such that OMCI frame decode works as expected during MIB uploads.
+
+Eventually I envision the **OMCI_CC** to be mostly hidden from an ONU device adapter, so
+a method to register these custom ME's needs to be provided.
+
+### deprecated.py
+
+This file contains some of the original _old-style_ OMCI frame creation and send
+commands for the Adtran ONU. These were originally copied over from the BroadCom
+ONU Device Adapter and modified for use in the Adtran ONU. After the **OMCI_CC** class
+was created to handle OMCI Tx/Rx, a reference to the **OMCI_CC** was passed in so that
+these methods could use the *OMCI_CC.send()* method
+
+If you look at the current Adtran ONU **pon_port.py** file, it still contains the original
+calls to these are still in place (commented out) next to how to do the same calls with
+the new **ME_Frame** and **OMCI_CC** classes.
##Unit Tests
-*TODO*: Add info on how to run unit tests on these files
\ No newline at end of file
+After some peer review and any needed refactoring of code, the plan is to create unit tests
+to cover the **OMCI_CC** and **ME_Frame** classes with a target of _90%+_ line coverage.
\ No newline at end of file
diff --git a/voltha/adapters/adtran_onu/omci/deprecated.py b/voltha/adapters/adtran_onu/omci/deprecated.py
new file mode 100644
index 0000000..f93df0b
--- /dev/null
+++ b/voltha/adapters/adtran_onu/omci/deprecated.py
@@ -0,0 +1,498 @@
+# 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
new file mode 100644
index 0000000..bac4c33
--- /dev/null
+++ b/voltha/adapters/adtran_onu/omci/me_frame.py
@@ -0,0 +1,212 @@
+#
+# 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.__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("param '{}' 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(str(operation).split('.')[1],
+ self.entity_class_name)
+
+ def _check_attributes(self, attributes, access):
+ for attribute in attributes:
+ index = self.entity_class.attribute_name_to_index_map.get(attribute)
+ # Bad attribute name (invalid or spelling error)?
+ assert index is not None, "Attribute '{}' is not valid for '{}'".format(attribute,
+ self.entity_class_name)
+ # Invalid access?
+ # TODO: Add read-only access to EntityClass _access set. Currently it is protected
+ assert access in self.entity_class.attributes[index]._access,\
+ "No '{} access for attribute '{}".format(str(access).split('.')[1], attribute)
+
+ @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.keys(), 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.keys(), 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)
+ ))
diff --git a/voltha/adapters/adtran_onu/omci/omci_cc.py b/voltha/adapters/adtran_onu/omci/omci_cc.py
index 4e08a61..9ee5a64 100644
--- a/voltha/adapters/adtran_onu/omci/omci_cc.py
+++ b/voltha/adapters/adtran_onu/omci/omci_cc.py
@@ -21,14 +21,18 @@
import sys
import arrow
from twisted.internet import reactor, defer
-from twisted.internet.defer import DeferredQueue, inlineCallbacks, returnValue, TimeoutError, CancelledError, failure, fail
+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
_ = third_party
-_MAX_INCOMING_OMCI_MESSAGES = 256
+_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
@@ -39,56 +43,70 @@
OP = EntityOperations
-class OMCISupport(object):
- """ Handle OMCI Specifics for Adtran ONUs"""
+class OMCI_CC(object):
+ """ Handle OMCI Communication Channel specifics for Adtran ONUs"""
- def __init__(self, handler, adapter, device_id):
+ 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._handler = handler
- self._adapter = adapter
+ self._adapter_agent = adapter_agent
self._device_id = device_id
self._proxy_address = None
self._tx_tid = 1
- self._deferred = None # TODO: Remove later if never used
self._enabled = False
self._requests = dict() # Tx ID -> (timestamp, deferred, tx_frame, timeout)
- self._onu_messages = DeferredQueue(size=_MAX_INCOMING_OMCI_MESSAGES)
+ 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, good rx resets this to 0
+ 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)
- def _cancel_deferred(self):
- d, self._deferred = self._deferred, None
- try:
- if d is not None and not d.called:
- d.cancel()
- except:
- pass
-
@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()
+ self._start()
else:
- self.stop()
+ self._stop()
@property
def tx_frames(self):
@@ -99,10 +117,26 @@
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
@@ -128,47 +162,105 @@
return int(round(avg * 1000.0)) # Milliseconds
@property
- def get_onu_autonomous_message(self):
+ def get_alarm_message(self):
"""
- Attempt to retrieve and remove an object from the ONU autonomous
- message queue.
+ Attempt to retrieve and remove an ONU Alarm Message from the ONU
+ autonomous message queue.
- :return: a Deferred which fires with the next OmciFrame available in
+ :return: a Deferred which fires with the next Alarm Frame available in
the queue.
"""
- return self._onu_messages.get()
+ return self._alarm_queue.get()
- def start(self):
+ @property
+ def get_avc_message(self):
+ """
+ Attempt to retrieve and remove an ONU Attribute Value Change (AVC)
+ Message from the ONU autonomous message queue.
+
+ :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.
+
+ :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 common startup tasks here
+ # TODO: Perform any other common startup tasks here
#
- self._cancel_deferred()
self.flush()
- device = self._adapter.adapter_agent.get_device(self._device_id)
+ device = self._adapter_agent.get_device(self._device_id)
self._proxy_address = device.proxy_address
- def stop(self):
+ 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._cancel_deferred()
self.flush()
self._proxy_address = None
- pass
+
+ # 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"""
- self.log.debug('rx-onu-frame', frame=rx_frame)
+ 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
- self._onu_messages.put((rx_frame, arrow.utcnow().float_timestamp))
+
+ 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
+ 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:
@@ -180,14 +272,19 @@
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:
+ # TODO: Investigate. Probably an unknown/unsupported ME
+ # 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', e=e)
+ self.log.exception('frame-decode', msg=hexlify(msg), e=e)
return
try:
@@ -206,11 +303,13 @@
# TODO: Could also validate response type based on request action
except KeyError:
- self.log.warn('message-missing', rx_id=rx_tid)
+ # 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-decode', e=e)
+ self.log.exception('frame-match', msg=hexlify(msg), e=e)
if d is not None:
return d.errback(failure.Failure(e))
return
@@ -233,14 +332,17 @@
self._requests = dict()
if max_age == 0:
- # Flush autonomous messages
- while self._onu_messages.pending:
- _ = yield self._onu_messages.get()
+ # 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 0 is reserved
- for autonomously generated message from an ONU
+ Get the next Transaction ID for a tx. Note TID=0 is reserved
+ for autonomously generated messages from an ONU
:return: (int) TID
"""
@@ -251,10 +353,15 @@
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:
- # tx_msg = None
timeout = 0
if isinstance(value, failure.Failure):
@@ -266,7 +373,44 @@
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
+ #
+ 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, \
@@ -284,18 +428,19 @@
tx_tid = self._get_tx_tid()
frame.fields['transaction_id'] = tx_tid
- assert tx_tid not in self._requests, 'TX TID is already exists'
+ 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.adapter_agent.send_proxied_message(self._proxy_address,
- hexify(str(frame)))
+ 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.addErrback(self._request_failure, tx_tid)
+ d.addCallbacks(self._request_success, self._request_failure,
+ errbackArgs=(tx_tid,))
if timeout > 0:
d.addTimeout(timeout, reactor)
@@ -308,21 +453,12 @@
return d
- def send_get_OntG(self, attribute, entity_id=0, timeout=DEFAULT_OMCI_TIMEOUT):
- self.log.debug('send_get_OntG')
- frame = OmciFrame(
- transaction_id=self._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 self.send(frame, timeout)
+ ###################################################################################
+ # TODO: The following three need to be ported to the new OMCI_CC and ME_Frame style
+ # or perhaps made into static methods in the base ME_Frame class.
def send_mib_reset(self, entity_id=0, timeout=DEFAULT_OMCI_TIMEOUT):
- self.log.debug('send_mib_reset')
+ self.log.debug('send-mib-reset')
frame = OmciFrame(
transaction_id=self._get_tx_tid(),
message_type=OmciMibReset.message_id,
@@ -333,357 +469,27 @@
)
return self.send(frame, timeout)
- def send_set_tcont(self, entity_id, alloc_id, timeout=DEFAULT_OMCI_TIMEOUT):
- data = dict(
- alloc_id=alloc_id
- )
+ def send_mib_upload(self, timeout=DEFAULT_OMCI_TIMEOUT):
+ self.log.debug('send-mib-upload')
frame = OmciFrame(
transaction_id=self._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
+ message_type=OmciMibUpload.message_id,
+ omci_message=OmciMibUpload(
+ entity_class=OntData.class_id,
+ entity_id=0
)
)
return self.send(frame, timeout)
- def send_create_gem_port_network_ctp(self, 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:
- self.log.error('invalid-gem-port-direction', direction=direction)
- raise ValueError('Invalid GEM port direction: {_dir}'.format(_dir=direction))
-
+ def send_mib_upload_next(self, seq_no, timeout=DEFAULT_OMCI_TIMEOUT):
+ self.log.debug('send-mib-upload-next')
frame = OmciFrame(
transaction_id=self._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 self.send(frame, timeout)
-
- def send_set_8021p_mapper_service_profile(self, 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=self._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 self.send(frame, timeout)
-
- def send_create_8021p_mapper_service_profile(self, entity_id, timeout=DEFAULT_OMCI_TIMEOUT):
- frame = OmciFrame(
- transaction_id=self._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 self.send(frame, timeout)
-
- def send_create_mac_bridge_service_profile(self, entity_id, timeout=DEFAULT_OMCI_TIMEOUT):
- frame = OmciFrame(
- transaction_id=self._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,
- learning_ind=True,
- priority=0x8000,
- max_age=20 * 256,
- hello_time=2 * 256,
- forward_delay=15 * 256,
- unknown_mac_address_discard=True
- )
- )
- )
- return self.send(frame, timeout)
-
- def send_create_gal_ethernet_profile(self, entity_id, max_gem_payload_size,
- timeout=DEFAULT_OMCI_TIMEOUT):
- frame = OmciFrame(
- transaction_id=self._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 self.send(frame, timeout)
-
- def send_create_gem_inteworking_tp(self, entity_id, gem_port_net_ctp_id,
- service_profile_id, timeout=DEFAULT_OMCI_TIMEOUT):
- frame = OmciFrame(
- transaction_id=self._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,
- gal_profile_pointer=0x1
- )
- )
- )
- return self.send(frame, timeout)
-
- def send_create_mac_bridge_port_configuration_data(self, entity_id, bridge_id,
- port_id, tp_type, tp_id,
- timeout=DEFAULT_OMCI_TIMEOUT):
- frame = OmciFrame(
- transaction_id=self._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 self.send(frame, timeout)
-
- def send_create_vlan_tagging_filter_data(self, entity_id, vlan_id,
- timeout=DEFAULT_OMCI_TIMEOUT):
- frame = OmciFrame(
- transaction_id=self._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 self.send(frame, timeout)
-
- # def send_get_device_info(self, attribute, entity_id=0, timeout=DEFAULT_OMCI_TIMEOUT):
- # # TODO: Can this be combined with send_get_circuit_pack above?
- # frame = OmciFrame(
- # transaction_id=self._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 self.send(frame, timeout)
-
- def send_set_adminState(self, entity_id, timeout=DEFAULT_OMCI_TIMEOUT):
- self.log.debug('send_set_AdminState')
- data = dict(
- administrative_state=0
- )
- frame = OmciFrame(
- transaction_id=self._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 self.send(frame, timeout)
-
- def send_get_SoftwareImage(self, attribute, entity_id=0, timeout=DEFAULT_OMCI_TIMEOUT):
- self.log.debug('send_get_SoftwareImage')
- frame = OmciFrame(
- transaction_id=self._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 self.send(frame, timeout)
-
- def send_create_extended_vlan_tagging_operation_configuration_data(self,
- entity_id,
- assoc_type,
- assoc_me,
- timeout=DEFAULT_OMCI_TIMEOUT):
- frame = OmciFrame(
- transaction_id=self._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 self.send(frame, timeout)
-
- def send_set_extended_vlan_tagging_operation_tpid_configuration_data(self,
- 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=self._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 self.send(frame, timeout)
-
- def send_set_extended_vlan_tagging_operation_vlan_configuration_data_untagged(self,
- 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=self._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 self.send(frame, timeout)
-
- def send_set_extended_vlan_tagging_operation_vlan_configuration_data_single_tag(self,
- 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=self._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
+ message_type=OmciMibUploadNext.message_id,
+ omci_message=OmciMibUploadNext(
+ entity_class=OntData.class_id,
+ entity_id=0,
+ command_sequence_number=seq_no
)
)
return self.send(frame, timeout)
diff --git a/voltha/adapters/adtran_onu/omci/omci_entities.py b/voltha/adapters/adtran_onu/omci/omci_entities.py
new file mode 100644
index 0000000..f38f55b
--- /dev/null
+++ b/voltha/adapters/adtran_onu/omci/omci_entities.py
@@ -0,0 +1,110 @@
+# 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.
+#
+""" Adtran vendor-specific OMCI Entities"""
+
+import inspect
+
+import sys
+from scapy.fields import ByteField, ShortField
+from scapy.fields import IntField, StrFixedLenField
+
+
+from voltha.extensions.omci.omci_entities import EntityClassAttribute, \
+ AttributeAccess, EntityOperations, EntityClass
+
+# abbreviations
+ECA = EntityClassAttribute
+AA = AttributeAccess
+OP = EntityOperations
+
+
+class OntSystem(EntityClass):
+ class_id = 65300
+ attributes = [
+ ECA(ShortField("managed_entity_id", None), {AA.R, AA.SBC}),
+ ]
+ mandatory_operations = {OP.Get}
+
+
+class VerizonOpenOMCI(EntityClass):
+ class_id = 65400
+ attributes = [
+ ECA(ShortField("managed_entity_id", None), {AA.R, AA.SBC}),
+ ]
+ mandatory_operations = {OP.Get}
+
+
+class TwdmSystemProfile(EntityClass):
+ class_id = 65401
+ attributes = [
+ ECA(ShortField("managed_entity_id", None), {AA.R, AA.SBC}),
+ ]
+ mandatory_operations = {OP.Get}
+
+
+class TwdmChannel(EntityClass):
+ class_id = 65402
+ attributes = [
+ ECA(ShortField("managed_entity_id", None), {AA.R, AA.SBC}),
+ ]
+ mandatory_operations = {OP.Get}
+
+
+class WatchdogConfigData(EntityClass):
+ class_id = 65403
+ attributes = [
+ ECA(ShortField("managed_entity_id", None), {AA.R, AA.SBC}),
+ ]
+ mandatory_operations = {OP.Get}
+
+
+class FlexibleConfigurationStatusPortal(EntityClass):
+ class_id = 65420
+ attributes = [
+ ECA(ShortField("managed_entity_id", None), {AA.R, AA.SBC}),
+ ]
+ mandatory_operations = {OP.Get, OP.Set, OP.Create, OP.Delete}
+
+
+class ONU3G(EntityClass):
+ class_id = 65422
+ attributes = [
+ # TODO: Fix access for all attributes below
+ ECA(ShortField("managed_entity_id", None), {AA.R, AA.SBC}),
+ ]
+ mandatory_operations = {OP.Set, OP.Get, OP.Create, OP.Delete}
+
+
+#################################################################################
+# 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)
+)
+
+onu_custom_entity_classes = [c for c in _onu_entity_classes_name_map.itervalues()]
+
+
+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, \
+ "Class ID '{}' already exists in the class map".format(entity_class.class_id)
+ entity_id_to_class_map[entity_class.class_id] = entity_class
+
+ entity_classes.extend(new_me_classes)
diff --git a/voltha/adapters/adtran_onu/omci/omci_me.py b/voltha/adapters/adtran_onu/omci/omci_me.py
index f51bbd8..c3716cf 100644
--- a/voltha/adapters/adtran_onu/omci/omci_me.py
+++ b/voltha/adapters/adtran_onu/omci/omci_me.py
@@ -14,186 +14,10 @@
# limitations under the License.
#
"""
-OMCI Message support
+OMCI Managed Entity Frame support
"""
from voltha.extensions.omci.omci import *
-
-# abbreviations
-OP = EntityOperations
-
-
-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
-
- # TODO: add a required attributes check list for various operations
- # that each derive class can set as required. Then check these
- # in the appropriate operation method in this class and assert
- # if something is missing
-
- def __str__(self):
- return '{}: Entity_ID: {}, Data: {}'.\
- format(type(self.entity_class), self._entity_id, self.data)
-
- @staticmethod
- def check_type(param, types):
- if not isinstance(param, types):
- raise TypeError("param '{}' should be a {}".format(param, types))
-
- @property
- def entity_class(self):
- """
- The Entity Class for this ME
- :return: (EntityClass) Entity class
- """
- return self._class
-
- @property
- def entity_id(self):
- """
- The Entity ID for this ME frame
- :return: (int) Entity ID (0..0xFFFF)
- """
- return self._entity_id
-
- def create(self):
- """
- Create a Create request frame for this ME
- :return: (OmciFrame) OMCI Frame
- """
- assert OP.Create in self.entity_class.mandatory_operations, \
- "Set not allowed for '{}'".format(self.entity_class)
- 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'
-
- 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
- """
- assert OP.Delete in self.entity_class.mandatory_operations, \
- "Delete not allowed for '{}'".format(self.entity_class)
-
- 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 OP.Set in self.entity_class.mandatory_operations, \
- "Set not allowed for '{}'".format(self.entity_class)
- 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'
-
- 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 OP.Get in self.entity_class.mandatory_operations, \
- "Get not allowed for '{}'".format(self.entity_class)
- 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
-
- 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)
- ))
-
- @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
+from me_frame import MEFrame
class CardholderFrame(MEFrame):
@@ -245,6 +69,30 @@
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.
@@ -264,6 +112,33 @@
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
@@ -275,6 +150,7 @@
interworking_option=None,
service_profile_pointer=None,
interworking_tp_pointer=None,
+ pptp_counter=None,
gal_profile_pointer=None,
attributes=None):
"""
@@ -319,11 +195,12 @@
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(gal_profile_pointer, [int, type(None)])
+ 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')
@@ -337,6 +214,9 @@
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')
@@ -351,19 +231,19 @@
data = data or dict()
if gem_port_network_ctp_pointer is not None:
- data[gem_port_network_ctp_pointer] = gem_port_network_ctp_pointer
+ data['gem_port_network_ctp_pointer'] = gem_port_network_ctp_pointer
if interworking_option is not None:
- data[interworking_option] = interworking_option
+ data['interworking_option'] = interworking_option
if service_profile_pointer is not None:
- data[service_profile_pointer] = service_profile_pointer
+ data['service_profile_pointer'] = service_profile_pointer
if interworking_tp_pointer is not None:
- data[interworking_tp_pointer] = interworking_tp_pointer
+ data['interworking_tp_pointer'] = interworking_tp_pointer
if gal_profile_pointer is not None:
- data[gal_profile_pointer] = gal_profile_pointer
+ data['gal_profile_pointer'] = gal_profile_pointer
super(GemInterworkingTpFrame, self).__init__(GemInterworkingTp,
entity_id,
@@ -408,10 +288,10 @@
_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)])
+ 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')
@@ -433,13 +313,13 @@
data = data or dict()
if port_id is not None:
- data[port_id] = port_id
+ data['port_id'] = port_id
if tcont_id is not None:
- data[tcont_id] = tcont_id
+ data['tcont_pointer'] = tcont_id
if direction is not None:
- data[direction] = direction
+ data['direction'] = _directions[str(direction).lower()]
if upstream_tm is not None:
- data[upstream_tm] = upstream_tm
+ data['traffic_management_pointer_upstream'] = upstream_tm
super(GemPortNetworkCtpFrame, self).__init__(GemPortNetworkCtp,
entity_id,
@@ -478,13 +358,13 @@
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)])
+ 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
+ data['tp_pointer'] = tp_pointer
if interwork_tp_pointers is not None:
assert all(isinstance(tp, int) and 0 <= tp <= 0xFFFF
@@ -495,11 +375,11 @@
data = dict()
for pbit in range(0, len(interwork_tp_pointers)):
- data['interwork_tp_pointer_for_p_bit_priority_'.format(pbit)] = \
+ 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)] = \
+ data['interwork_tp_pointer_for_p_bit_priority_{}'.format(pbit)] = \
interwork_tp_pointers[len(interwork_tp_pointers) - 1]
super(Ieee8021pMapperServiceProfileFrame, self).__init__(Ieee8021pMapperServiceProfile,
@@ -547,10 +427,10 @@
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)])
+ 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')
@@ -574,21 +454,42 @@
data = data or dict()
if bridge_id_pointer is not None:
- data[bridge_id_pointer] = bridge_id_pointer
+ data['bridge_id_pointer'] = bridge_id_pointer
if port_num is not None:
- data[port_num] = port_num
+ data['port_num'] = port_num
if tp_type is not None:
- data[tp_type] = tp_type
+ data['tp_type'] = tp_type
if tp_pointer is not None:
- data[tp_pointer] = tp_pointer
+ 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.
@@ -625,7 +526,7 @@
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):
+ def __init__(self, entity_id, attributes=None):
"""
:param entity_id: (int) This attribute uniquely identifies each instance of
this managed entity. (0..65535)
@@ -672,8 +573,8 @@
2 - WRR - Weighted round robin
"""
# Validate
- self.check_type(alloc_id, [int, type(None)])
- self.check_type(policy, [int, type(None)])
+ 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')
@@ -687,10 +588,10 @@
data = dict()
if alloc_id is not None:
- data[alloc_id] = alloc_id
+ data['alloc_id'] = alloc_id
if policy is not None:
- data[policy] = policy
+ data['policy'] = policy
super(TcontFrame, self).__init__(Tcont, entity_id, data)
@@ -713,30 +614,32 @@
"""
# Validate
- self.check_type(vlan_tcis, [list, type(None)])
- self.check_type(forward_operation, [int, type(None)])
+ 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:
- data[forward_operation] = forward_operation
+ assert 0 <= forward_operation <= 0x21, 'forwarding_operation must be 0x00..0x21'
+ data['forward_operation'] = forward_operation
super(VlanTaggingFilterDataFrame, self).__init__(VlanTaggingFilterData,
entity_id,
data)
-
-
-# TODO: Wednesday - Start with send_create_extended_vlan_tagging_operation_configuration_data
diff --git a/voltha/adapters/adtran_onu/onu_gem_port.py b/voltha/adapters/adtran_onu/onu_gem_port.py
index b0aecd1..c0fb759 100644
--- a/voltha/adapters/adtran_onu/onu_gem_port.py
+++ b/voltha/adapters/adtran_onu/onu_gem_port.py
@@ -60,14 +60,15 @@
omci = None # TODO: Get from handler
@staticmethod
- def create(handler, gem_port):
+ def create(handler, gem_port, is_mock=False):
return OnuGemPort(gem_port['gemport-id'],
None,
encryption=gem_port['encryption'], # aes_indicator,
tcont_ref=gem_port['tcont-ref'],
name=gem_port['name'],
traffic_class=gem_port['traffic-class'],
- handler=handler)
+ handler=handler,
+ is_mock=is_mock)
@inlineCallbacks
def add_to_hardware(self, omci):
@@ -86,12 +87,12 @@
# 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 = 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
@@ -106,9 +107,10 @@
# 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 = 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
+ 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
except Exception as e:
self.log.exception('interworking-create', e=e)
raise
@@ -117,8 +119,9 @@
# 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 = omci.send_set_8021p_mapper_service_profile(tcont.entity_id, # Entity ID
- self.gem_id) # Interworking TP 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
@@ -152,4 +155,4 @@
# 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
\ No newline at end of file
+ pass # TODO: Implement me
diff --git a/voltha/adapters/adtran_onu/onu_pm_metrics.py b/voltha/adapters/adtran_onu/onu_pm_metrics.py
index c4078bf..ac0b02e 100644
--- a/voltha/adapters/adtran_onu/onu_pm_metrics.py
+++ b/voltha/adapters/adtran_onu/onu_pm_metrics.py
@@ -27,7 +27,11 @@
('tx_frames', PmConfig.COUNTER),
('tx_errors', PmConfig.COUNTER),
('rx_frames', PmConfig.COUNTER),
- ('rx_onu_frames', PmConfig.COUNTER),
+ ('rx_unknown_tid', PmConfig.COUNTER),
+ ('rx_onu_frames', PmConfig.COUNTER), # Rx ONU autonomouse messages
+ ('rx_alarm_overflow', PmConfig.COUNTER), # Autonomous ONU generated alarm message overflows
+ ('rx_avc_overflow', PmConfig.COUNTER), # Autonomous ONU generated AVC message overflows
+ ('rx_onu_discards', PmConfig.COUNTER), # Autonomous ONU message unknown type discards
('rx_timeouts', PmConfig.COUNTER),
('consecutive_errors', PmConfig.COUNTER),
('reply_min', PmConfig.GUAGE), # Milliseconds
diff --git a/voltha/adapters/adtran_onu/onu_tcont.py b/voltha/adapters/adtran_onu/onu_tcont.py
index ec51246..3d3e2bf 100644
--- a/voltha/adapters/adtran_onu/onu_tcont.py
+++ b/voltha/adapters/adtran_onu/onu_tcont.py
@@ -38,7 +38,7 @@
return self._entity_id
@staticmethod
- def create(handler, tcont, td):
+ def create(handler, tcont, td, is_mock=False):
assert isinstance(tcont, dict), 'TCONT should be a dictionary'
assert isinstance(td, TrafficDescriptor), 'Invalid Traffic Descriptor data type'
@@ -50,7 +50,8 @@
td,
entity_id,
name=tcont['name'],
- vont_ani=tcont['vont-ani'])
+ vont_ani=tcont['vont-ani'],
+ is_mock=is_mock)
@inlineCallbacks
def add_to_hardware(self, omci):
@@ -62,8 +63,9 @@
#
# NOTE: Entity ID should be computed. For NGPON2, they were starting
# at 256 and incrementing.
- results = yield self._handler.omci.send_set_tcont(self._entity_id, # Entity ID
- self.alloc_id) # Alloc ID
+ results = None
+ # results = yield self._handler.omci.send_set_tcont(self._entity_id, # Entity ID
+ # self.alloc_id) # Alloc ID
# response = yield omci.send(TcontFrame(self._entity_id,
# alloc_id=self.alloc_id).get())
@@ -79,7 +81,8 @@
if self._is_mock:
returnValue('mock')
- results = yield omci.send(TcontFrame(self._entity_id).delete())
+ results = None
+ # results = yield omci.send(TcontFrame(self._entity_id).delete())
returnValue(results)
diff --git a/voltha/adapters/adtran_onu/onu_traffic_descriptor.py b/voltha/adapters/adtran_onu/onu_traffic_descriptor.py
index d24de7b..d41d632 100644
--- a/voltha/adapters/adtran_onu/onu_traffic_descriptor.py
+++ b/voltha/adapters/adtran_onu/onu_traffic_descriptor.py
@@ -46,12 +46,12 @@
else:
best_effort = None
- return TrafficDescriptor(traffic_disc['fixed-bandwidth'],
- traffic_disc['assured-bandwidth'],
- traffic_disc['maximum-bandwidth'],
- name=traffic_disc['name'],
- best_effort=best_effort,
- additional=additional)
+ return OnuTrafficDescriptor(traffic_disc['fixed-bandwidth'],
+ traffic_disc['assured-bandwidth'],
+ traffic_disc['maximum-bandwidth'],
+ name=traffic_disc['name'],
+ best_effort=best_effort,
+ additional=additional)
@inlineCallbacks
def add_to_hardware(self, omci):
diff --git a/voltha/adapters/adtran_onu/pon_port.py b/voltha/adapters/adtran_onu/pon_port.py
index 3bbbe67..4b6c044 100644
--- a/voltha/adapters/adtran_onu/pon_port.py
+++ b/voltha/adapters/adtran_onu/pon_port.py
@@ -22,6 +22,18 @@
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.
+#
+#
+###################################################################################
+
_STARTUP_RETRY_WAIT = 5
BRDCM_DEFAULT_VLAN = 4091 # TODO: Deprecate later...
@@ -49,6 +61,8 @@
self._gem_ports = {} # gem-id -> GemPort
self._tconts = {} # alloc-id -> TCont
+ # TODO: Until we have an external database, just save it here
+ self.mib_data_store = dict() # TODO: Improve and make class attribute/property
# TODO: Add stats, alarm reference, ...
pass
@@ -69,8 +83,8 @@
self._update_adapter_agent()
# Begin ONU Activation sequence
- self._deferred = reactor.callLater(_STARTUP_RETRY_WAIT, self.message_exchange)
-
+ self._deferred = reactor.callLater(_STARTUP_RETRY_WAIT,
+ self._initial_message_exchange)
# TODO: start h/w sync
pass
@@ -134,8 +148,8 @@
type=Port.PON_ONU,
admin_state=self._admin_state,
oper_status=self._oper_status,
- peers = [Port.PeerPort(device_id=device.parent_id,
- port_no=device.parent_port_no)])
+ peers=[Port.PeerPort(device_id=device.parent_id,
+ port_no=device.parent_port_no)])
return self._port
def _update_adapter_agent(self):
@@ -144,8 +158,12 @@
pass
@inlineCallbacks
- def message_exchange(self):
- self.log.info('message-exchange')
+ 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
if self._handler.device_id is None or not self.enabled:
@@ -167,6 +185,7 @@
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)
@@ -177,20 +196,12 @@
returnValue('no-device')
try:
- # May timeout to ONU not fully discovered (can happen in xPON case)
+ # 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_get_OntG('vendor_id')
-
- 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)
-
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"]
@@ -203,7 +214,6 @@
device.connect_status = ConnectStatus.REACHABLE
self._handler.adapter_agent.update_device(device)
- # response = yield omci.send_get_cardHolder('actual_plugin_unit_type', 257)
response = yield omci.send(CardholderFrame(True, 1,
'actual_plugin_unit_type').get())
@@ -211,7 +221,6 @@
data = omci_response.getfieldval("data")
# device.type = str(data["actual_plugin_unit_type"])
- # response = yield omci.send_get_circuit_pack('number_of_ports', 257)
response = yield omci.send(CircuitPackFrame(257, 'number_of_ports').get())
omci_response = response.getfieldval("omci_message")
@@ -219,14 +228,12 @@
num_ports = data["number_of_ports"]
assert num_ports == 1, 'Invalid number of ports: {}'.format(num_ports)
- # response = yield omci.send_get_IpHostConfigData('mac_address', 515)
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_get_Ont2G('equipment_id', 0)
response = yield omci.send(Ont2GFrame('equipment_id').get())
omci_response = response.getfieldval("omci_message")
@@ -243,7 +250,6 @@
# decimal version
omciVersion = str(data["omcc_version"])
- # response = yield omci.send_get_Ont2G('vendor_product_code', 0)
response = yield omci.send(Ont2GFrame('vendor_product_code').get())
omci_response = response.getfieldval("omci_message")
@@ -251,13 +257,18 @@
# decimal value
vendorProductCode = str(data["vendor_product_code"])
- # response = yield omci.send(OntGFrame('version').get())
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)
#
@@ -279,17 +290,18 @@
# self.adapter_agent.update_device(device)
device.oper_status = OperStatus.ACTIVE
device.connect_status = ConnectStatus.REACHABLE
+ device.reason = 'Initial OMCI message exchange complete'
self._handler.adapter_agent.update_device(device)
- # Start up non-critical message exchange
- self._deferred = reactor.callLater(0, self.message_exchange_part_2)
+ # Start MIB synchronization
+ self._deferred = reactor.callLater(0, self._perform_mib_upload)
self.log.info('onu-activated')
# These exceptions are not recoverable
- except (AssertionError, TypeError, ValueError, AttributeError) as e:
+ except (TypeError, ValueError) as e:
self.log.exception('Failed', e=e)
device.oper_status = OperStatus.FAILED
- device.reason = e.message
+ device.reason = 'Initial message sequence failure: ' + e.message
self._handler.adapter_agent.update_device(device)
except TimeoutError as e:
@@ -297,20 +309,117 @@
self._handler.adapter_agent.update_device(device)
# Try again later. May not have been discovered
self._deferred = reactor.callLater(_STARTUP_RETRY_WAIT,
- self.message_exchange)
+ self._initial_message_exchange)
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.message_exchange)
+ self._initial_message_exchange)
@inlineCallbacks
- def message_exchange_part_2(self):
- """ Called after basic OMCI message startup/exchange """
+ def _perform_mib_upload(self):
+ """
+ Called after basic OMCI MIB RESET message startup/exchange.
- self.log.info('message-exchange-part-2')
+ 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
+ omci = self._handler.omci
+
+ if self._handler.is_mock:
+ self._deferred = reactor.callLater(0, self._perform_mib_download)
+ returnValue('is-mock')
+
+ try:
+ device = self._handler.adapter_agent.get_device(self._handler.device_id)
+ device.reason = 'Performing MIB Synchronization'
+ 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()
+
+ ########################################
+ # Begin MIB Upload
+ results = yield omci.send_mib_upload()
+ number_of_commands = results.fields['omci_message'].fields['number_of_commands']
+
+ for seq_no in xrange(number_of_commands):
+ results = yield omci.send_mib_upload_next(seq_no)
+
+ 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']
+
+ key = (object_entity_class, object_entity_id)
+
+ if key not in self.mib_data_store:
+ self.mib_data_store[key] = (object_attributes_mask, object_data)
+ else:
+ pass
+
+ # Successful if here
+
+ device.reason = 'MIB Synchronization Complete'
+ self._handler.adapter_agent.update_device(device)
+
+ # Start up non-critical message exchange
+ self._deferred = reactor.callLater(0, self._perform_mib_download)
+ self.log.info('mib-synchronized')
+
+ except TimeoutError as e:
+ self.log.warn('mib-upload', e=e)
+
+ if device is not None:
+ device.reason = 'mib-upload-failure: Response Timeout'
+ self._handler.adapter_agent.update_device(device)
+
+ # Try again later
+ self._deferred = reactor.callLater(_STARTUP_RETRY_WAIT,
+ self._initial_message_exchange)
+
+ 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)
+
+ # 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:
@@ -318,100 +427,443 @@
omci = self._handler.omci
- try:
- # reset incoming message queue
- omci.flush()
- device = self._handler.adapter_agent.get_device(self._handler.device_id)
+ if self._handler.is_mock:
+ self._bridge_initialized = True
+ self._deferred = reactor.callLater(0, self._sync_existing_xpon)
+ returnValue('is-mock')
- except Exception as e:
- self.log.exception('top-of-msg-exch', e=e)
- device = None
+ # 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:
- cvid = BRDCM_DEFAULT_VLAN # TODO: What should this be?
+ ################################################################################
+ #
+ #
+ # EntityID will be referenced by:
+ # -
+ # -
+ # -
+ # -
+ # References:
+ # -
+ # -
- # construct message
- # MIB Reset - OntData - 0
- # results = yield omci.send_mib_reset()
+ 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
- # Create AR - GalEthernetProfile - 1
- results = yield omci.send_create_gal_ethernet_profile(
- 1, # Entity ID
- 48) # Max GEM Payload size
+ 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)
- # success = results.fields['omci_message'].fields['success_code'] == 0
- # error_mask = results.fields['omci_message'].fields['parameter_error_attributes_mask']
+ ################################################################################
+ direction = "bi-directional"
- # Port 2
- # Extended VLAN Tagging Operation config
- # TODO: add entry here for additional UNI interfaces
- results = yield omci.send_create_extended_vlan_tagging_operation_configuration_data(
- 0x202, # Entity ID
- 2, # Assoc Type
- 0x102) # Assoc ME
+ # 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
- # success = results.fields['omci_message'].fields['success_code'] == 0
- # error_mask = results.fields['omci_message'].fields['parameter_error_attributes_mask']
+ 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)
- # Set AR - ExtendedVlanTaggingOperationConfigData - 514 - 8100 - 8100
- results = yield omci.send_set_extended_vlan_tagging_operation_tpid_configuration_data(
- 0x202, # Entity ID
- 0x8100, # input TPID
- 0x8100) # output TPID
+ ################################################################################
+ # GEM Interworking config
+ #
+ #
+ # EntityID will be referenced by:
+ # -
+ # -
+ # -
+ # -
+ # References:
+ # -
+ # -
+ # TODO: for the service_profile_pointer=0x100, is this create/set somewhere later
- # success = results.fields['omci_message'].fields['success_code'] == 0
- # unsupported_mask = results.fields['omci_message'].fields['unsupported_attributes_mask']
- # failed_mask = results.fields['omci_message'].fields['failed_attributes_mask']
+ 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
- # MAC Bridge Service config
- results = yield omci.send_create_mac_bridge_service_profile(0x201) # Entity 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)
- # success = results.fields['omci_message'].fields['success_code'] == 0
- # error_mask = results.fields['omci_message'].fields['parameter_error_attributes_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
- # Create AR - MacBridgePortConfigData
- results = yield omci.send_create_mac_bridge_port_configuration_data(
- 0x201, # Entity ID
- 0x201, # Bridge ID
- 2, # Port ID
- 1, # TP Type
- 0x102) # TP ID
+ 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
- # success = results.fields['omci_message'].fields['success_code'] == 0
- # error_mask = results.fields['omci_message'].fields['parameter_error_attributes_mask']
+ 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)
- # Mapper Service config
- results = yield omci.send_create_8021p_mapper_service_profile(0x8001) # Entity ID
+ ################################################################################
+ # MAC Bridge Service Profile - Once per UNI
+ #
+ # EntityID will be referenced by:
+ # - MAC Bridge Port Configuration Data
+ # References:
+ # - Nothing
- # success = results.fields['omci_message'].fields['success_code'] == 0
- # error_mask = results.fields['omci_message'].fields['parameter_error_attributes_mask']
+ 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
- results = yield omci.send_create_mac_bridge_port_configuration_data(
- 0x2102, # Entity ID
- 0x201, # Bridge ID
- 3, # Port ID
- 3, # TP Type
- 0x8001) # TP ID
+ # This configuration is for Ethernet UNI
+ #
+ # EntityID will be referenced by:
+ # -
+ # -
+ # -
+ # -
+ # References:
+ # - MAC Bridge Service Profile (the bridge)
+ # - PPTP Ethernet UNI
- # success = results.fields['omci_message'].fields['success_code'] == 0
- # error_mask = results.fields['omci_message'].fields['parameter_error_attributes_mask']
+ 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
- results = yield omci.send_create_vlan_tagging_filter_data(0x2102, # Entity ID
- cvid) # VLAN ID
+ # Set anything, this request will not be used when using Extended Vlan
- # success = results.fields['omci_message'].fields['success_code'] == 0
- # error_mask = results.fields['omci_message'].fields['parameter_error_attributes_mask']
+ 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(
- # 0x202, # Entity ID
+ # 0x200, # Entity ID
# 8, # Filter Inner Priority
# 0, # Filter Inner VID
# 0, # Filter Inner TPID DE
@@ -421,103 +873,58 @@
#
# 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(
- 0x202, # Entity ID
- 0x1000, # Filter Inner VID
- cvid) # Treatment inner 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
+ # If here, we can add TCONTs/GEM Ports/... as needed
self._bridge_initialized = True
- self._deferred = reactor.callLater(0, self.sync_existing_xpon)
- # that xPON may have already sent us
+ self._deferred = reactor.callLater(0, self._sync_existing_xpon)
- # ###############################################################################
- # # Multicast related MEs
- # # Set AR - MulticastOperationsProfile - Dynamic Access Control List table
- # # Create AR - MacBridgePortConfigData - 9000 - 513 - 6 - 6 - 6
- # results = yield omci.send_create_mac_bridge_port_configuration_data(
- # 0x2328,
- # 0x201,
- # 6,
- # 6,
- # 6)
- #
- # # Multicast Operation Profile config
- # # Create AR - MulticastOperationsProfile
- # results = yield omci.send_create_multicast_operations_profile(
- # 0x201,
- # 3)
- #
- # # Multicast Subscriber config
- # # Create AR - MulticastSubscriberConfigInfo
- # results = yield omci.send_create_multicast_subscriber_config_info(
- # 0x201,
- # 0,
- # 0x201)
- #
- # # Create AR - GemPortNetworkCtp - 260 - 4000 - 0 Multicast
- # results = yield omci.send_create_gem_port_network_ctp(
- # 0x104,
- # 0x0FA0,
- # 0,
- # "downstream",
- # 0)
- #
- # # Multicast GEM Interworking config Multicast
- # # Create AR - MulticastGemInterworkingTp - 6 - 260
- # results = yield omci.send_create_multicast_gem_interworking_tp(0x6, 0x104)
- #
- # results = yield omci.send_set_multicast_operations_profile_acl_row0(
- # 0x201,
- # 'dynamic',
- # 0,
- # 0x0fa0,
- # 0x0fa0,
- # '0.0.0.0',
- # '224.0.0.0',
- # '239.255.255.255')
- #
- # # Multicast Operation Profile config
- # # Set AR - MulticastOperationsProfile - Downstream IGMP Multicast TCI
- # results = yield omci.send_set_multicast_operations_profile_ds_igmp_mcast_tci(
- # 0x201,
- # 4,
- # cvid)
-
- except AssertionError as e:
- self.log.exception('Failed', e=e)
- # TODO: get message and report back
- # TODO: get message and report back
+ 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.debug('Failed', e=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. TODO: Do we want to restart at part 1 here ?
+
+ # Try again later
self._deferred = reactor.callLater(_STARTUP_RETRY_WAIT,
- self.message_exchange_part_2)
+ self._initial_message_exchange)
@inlineCallbacks
- def sync_existing_xpon(self):
+ 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)
+ # 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')
@@ -658,14 +1065,3 @@
raise
returnValue(results)
-
-
-
-
-
-
-
-
-
-
-
diff --git a/voltha/adapters/adtran_onu/uni_port.py b/voltha/adapters/adtran_onu/uni_port.py
index b93bbb9..43ccfee 100644
--- a/voltha/adapters/adtran_onu/uni_port.py
+++ b/voltha/adapters/adtran_onu/uni_port.py
@@ -138,15 +138,11 @@
:return: VOLTHA Port object
"""
if self._port is None:
- device = self._handler.adapter_agent.get_device(self._handler.device_id)
-
self._port = Port(port_no=self.port_number,
label='Ethernet port',
type=Port.ETHERNET_UNI,
admin_state=self._admin_state,
- oper_status=self._oper_status,
- peers=[Port.PeerPort(device_id=device.parent_id,
- port_no=device.parent_port_no)])
+ oper_status=self._oper_status)
return self._port
def add_logical_port(self, openflow_port_no, control_vlan=None,