blob: 8c92c9b6d3c4c5604080d9eea202e0469c707ab8 [file] [log] [blame]
# Copyright 2017-present Adtran, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import structlog
from traffic_descriptor import TrafficDescriptor
from voltha.protos.bbf_fiber_base_pb2 import \
OntaniConfig, VOntaniConfig, VEnetConfig
from voltha.protos.bbf_fiber_tcont_body_pb2 import TcontsConfigData
from voltha.protos.bbf_fiber_traffic_descriptor_profile_body_pb2 import TrafficDescriptorProfileData
from voltha.protos.bbf_fiber_gemport_body_pb2 import GemportsConfigData
from voltha.protos.bbf_fiber_multicast_gemport_body_pb2 import \
MulticastGemportsConfigData
from voltha.protos.bbf_fiber_multicast_distribution_set_body_pb2 import \
MulticastDistributionSetData
log = structlog.get_logger()
class AdtranXPON(object):
"""
Class to abstract common OLT and ONU xPON operations
"""
def __init__(self, **kwargs):
# xPON config dictionaries
self._v_ont_anis = {} # Name -> dict
self._ont_anis = {} # Name -> dict
self._v_enets = {} # Name -> dict
self._tconts = {} # Name -> dict
self._traffic_descriptors = {} # Name -> dict
self._gem_ports = {} # Name -> dict
self._mcast_gem_ports = {} # Name -> dict
self._mcast_dist_sets = {} # Name -> dict
self._cached_xpon_pon_info = {} # PON-id -> dict
@property
def v_ont_anis(self):
return self._v_ont_anis
@property
def ont_anis(self):
return self._ont_anis
@property
def v_enets(self):
return self._v_enets
@property
def tconts(self):
return self._tconts
@property
def traffic_descriptors(self):
return self._traffic_descriptors
@property
def gem_ports(self):
return self._gem_ports
def _get_xpon_collection(self, data):
"""
Get the collection for the object type and handler routines
:param data: xPON object
"""
if isinstance(data, OntaniConfig):
return self.ont_anis, \
self.on_ont_ani_create,\
self.on_ont_ani_modify, \
self.on_ont_ani_delete
elif isinstance(data, VOntaniConfig):
return self.v_ont_anis, \
self.on_vont_ani_create,\
self.on_vont_ani_modify, \
self.on_vont_ani_delete
elif isinstance(data, VEnetConfig):
return self.v_enets, \
self.on_venet_create,\
self.on_venet_modify, \
self.on_venet_delete
elif isinstance(data, TcontsConfigData):
return self.tconts, \
self.on_tcont_create,\
self.on_tcont_modify, \
self.on_tcont_delete
elif isinstance(data, TrafficDescriptorProfileData):
return self.traffic_descriptors, \
self.on_td_create,\
self.on_td_modify, \
self.on_td_delete
elif isinstance(data, GemportsConfigData):
return self.gem_ports, \
self.on_gemport_create,\
self.on_gemport_modify, \
self.on_gemport_delete
elif isinstance(data, MulticastGemportsConfigData):
return self.mcast_gem_ports, \
self.on_mcast_gemport_create,\
self.on_mcast_gemport_modify, \
self.on_mcast_gemport_delete
elif isinstance(data, MulticastDistributionSetData):
return self.mcast_dist_sets, \
self.on_mcast_dist_set_create,\
self.on_mcast_dist_set_modify, \
self.on_mcast_dist_set_delete
return None, None, None, None
def _data_to_dict(self, data, td=None):
if isinstance(data, OntaniConfig):
name = data.name
interface = data.interface
inst_data = data.data
return 'ont-ani', {
'name': name,
'description': interface.description,
'enabled': interface.enabled,
'upstream-fec': inst_data.upstream_fec_indicator,
'mgnt-gemport-aes': inst_data.mgnt_gemport_aes_indicator,
'data': data
}
elif isinstance(data, VOntaniConfig):
name = data.name
interface = data.interface
inst_data = data.data
return 'vOnt-ani', {
'name': name,
'description': interface.description,
'enabled': interface.enabled,
'onu-id': inst_data.onu_id,
'expected-serial-number': inst_data.expected_serial_number,
'expected-registration-id': inst_data.expected_registration_id,
'preferred-channel-pair': inst_data.preferred_chanpair,
'channel-partition': inst_data.parent_ref,
'upstream-channel-speed': inst_data.upstream_channel_speed,
'data': data
}
elif isinstance(data, VEnetConfig):
name = data.name
interface = data.interface
inst_data = data.data
return 'vEnet', {
'name': name,
'description': interface.description,
'enabled': interface.enabled,
'vont-ani': inst_data.v_ontani_ref,
'data': data
}
elif isinstance(data, TcontsConfigData):
return 'TCONT', {
'name': data.name,
'alloc-id': data.alloc_id,
'vont-ani': data.interface_reference,
'td-ref': td['name'],
'data': data
}
elif isinstance(data, TrafficDescriptorProfileData):
additional = TrafficDescriptor.AdditionalBwEligibility.from_value(
data.additional_bw_eligibility_indicator)
return 'Traffic-Desc', {
'name': data.name,
'fixed-bandwidth': data.fixed_bandwidth,
'assured-bandwidth': data.assured_bandwidth,
'maximum-bandwidth': data.maximum_bandwidth,
'priority': data.priority,
'weight': data.weight,
'additional-bw-eligibility-indicator': additional,
'data': data
}
elif isinstance(data, GemportsConfigData):
return 'GEMPort', {
'name': data.name,
'gemport-id': data.gemport_id,
'tcont-ref': data.tcont_ref,
'encryption': data.aes_indicator,
'traffic-class': data.traffic_class,
'venet-ref': data.itf_ref, # vENET
'data': data
}
elif isinstance(data, MulticastGemportsConfigData):
return 'MCAST-GEM', {
'name': data.name,
'gemport-id': data.gemport_id,
'traffic-class': data.traffic_class,
'is-broadcast': data.is_broadcast,
'channel-pair-ref': data.itf_ref, # channel-pair
'data': data
}
elif isinstance(data, MulticastDistributionSetData):
data_dict = {
'name': data.name,
'multicast-gemport-ref': data.multicast_gemport_ref,
'multicast-vlans-all': None,
'multicast-vlans-list': [],
'data': data
}
assert True is False, 'Need to decode multicast-vlans parameter'
return 'MCAST-Distribution', data_dict
return None
def create_tcont(self, tcont_data, traffic_descriptor_data):
"""
Create TCONT information
:param tcont_data:
:param traffic_descriptor_data:
"""
log.debug('create-tcont', tcont=tcont_data, td=traffic_descriptor_data)
# Handle TD first, then TCONT
try:
self.xpon_create(traffic_descriptor_data)
except Exception as e:
log.exception('td-create', td=traffic_descriptor_data)
try:
td = self.traffic_descriptors.get(traffic_descriptor_data.name)
self.xpon_create(tcont_data, td=td)
except Exception as e:
log.exception('tcont-create', tcont=tcont_data)
def update_tcont(self, tcont_data, traffic_descriptor_data):
"""
Update TCONT information
:param tcont_data:
:param traffic_descriptor_data:
"""
log.debug('update-tcont', tcont=tcont_data, td=traffic_descriptor_data)
# Handle TD first, then TCONT. The TD may be new
try:
items, _, _, _ = self._get_xpon_collection(traffic_descriptor_data)
existing_item = items.get(traffic_descriptor_data.name)
if existing_item is None:
self.xpon_create(traffic_descriptor_data)
else:
self.xpon_update(traffic_descriptor_data)
except Exception as e:
log.exception('td-update', td=traffic_descriptor_data)
try:
self.xpon_update(tcont_data)
except Exception as e:
log.exception('tcont-update', tcont=tcont_data)
def remove_tcont(self, tcont_data, traffic_descriptor_data):
"""
Remove TCONT information
:param tcont_data:
:param traffic_descriptor_data:
"""
log.debug('remove-tcont', tcont=tcont_data, td=traffic_descriptor_data)
# Handle TCONT first when removing, then TD
try:
self.xpon_remove(traffic_descriptor_data)
except Exception as e:
log.exception('td-update', td=traffic_descriptor_data)
try:
self.xpon_remove(tcont_data)
except Exception as e:
log.exception('tcont-update', tcont=tcont_data)
def xpon_create(self, data, td=None):
log.debug('xpon-create', data=data)
name = data.name
items, create_method, update_method, _ = self._get_xpon_collection(data)
if items is None:
raise ValueError('Unknown data type: {}'.format(type(data)))
item_type, new_item = self._data_to_dict(data, td=td)
if name in items:
# Treat like an update. It will update collection if needed
return self.xpon_update(data, td=td)
log.debug('new-item', item_type=item_type, item=new_item)
items[name] = new_item
self._cached_xpon_pon_info = {} # Clear cached data
if create_method is not None:
try:
new_item = create_method(new_item)
except Exception as e:
log.exception('xpon-create', item=new_item, e=e)
if new_item is not None:
items[name] = new_item
else:
del items[name]
def xpon_update(self, data, td=None):
log.debug('xpon-update', data=data)
name = data.name
items, create, update_method, delete = self._get_xpon_collection(data)
if items is None:
raise ValueError('Unknown data type: {}'.format(type(data)))
existing_item = items.get(name)
if existing_item is None:
raise KeyError("'{}' not found. Type: {}".format(name, type(data)))
item_type, update_item = self._data_to_dict(data, td=td)
log.debug('update-item', item_type=item_type, item=update_item)
def _dict_diff(lhs, rhs):
"""
Compare the values of two dictionaries and return the items in 'rhs'
that are different than 'lhs. The RHS dictionary keys can be a subset of the
LHS dictionary, or the RHS dictionary keys can contain new values.
:param lhs: (dict) Original dictionary values
:param rhs: (dict) New dictionary values to compare to the original (lhs) dict
:return: (dict) Dictionary with differences from the RHS dictionary
"""
lhs_keys = {k for k in lhs.keys() if k not in ['object', 'data']}
rhs_keys = {k for k in rhs.keys() if k not in ['object', 'data']}
assert len(lhs_keys) == len(lhs_keys & rhs_keys), 'Dictionary Keys do not match'
return {k: v for k, v in rhs.items() if k not in lhs or lhs[k] != rhs[k]}
# Calculate the difference
diffs = _dict_diff(existing_item, update_item)
if len(diffs) == 0:
log.debug('update-item-no-diffs')
return
items[name] = update_item
self._cached_xpon_pon_info = {} # Clear cached data
# Act on any changed items
if update_method is not None:
try:
update_item = update_method(existing_item, update_item, diffs)
except Exception as e:
log.exception('xpon-update', existing=existing_item,
update=update_item, diffs=diffs,
e=e)
if update_item is not None:
items[name] = update_item
else:
del items[name]
def xpon_remove(self, data):
log.debug('xpon_remove', data=data)
name = data.name
items, create, update, delete_method = self._get_xpon_collection(data)
item = items.get(name)
if item is not None:
if delete_method is None:
item = None
else:
try:
item = delete_method(item)
except Exception as e:
log.exception('xpon-remove', item=items, e=e)
self._cached_xpon_pon_info = {} # Clear cached data
if item is None:
del items[name]
else:
# Update item in collection (still referenced somewhere)
items[name] = item
def on_ont_ani_create(self, ont_ani):
"""
A new ONT-ani is being created. You can override this method to
perform custom operations as needed. If you override this method, you can add
additional items to the item dictionary to track additional implementation
key/value pairs.
:param ont_ani: (dict) new ONT-ani
:return: (dict) Updated ONT-ani dictionary, None if item should be deleted
"""
return ont_ani # Implement in your OLT, if needed
def on_ont_ani_modify(self, ont_ani, update, diffs):
"""
A existing ONT-ani is being updated. You can override this method to
perform custom operations as needed. If you override this method, you can add
additional items to the item dictionary to track additional implementation
key/value pairs.
:param ont_ani: (dict) existing ONT-ani item dictionary
:param update: (dict) updated (changed) ONT-ani
:param diffs: (dict) collection of items different in the update
:return: (dict) Updated ONT-ani dictionary, None if item should be deleted
"""
return update # Implement in your OLT, if needed
def on_ont_ani_delete(self, ont_ani):
"""
A existing ONT-ani is being deleted. You can override this method to
perform custom operations as needed. If you override this method, you can add
additional items to the item dictionary to track additional implementation
key/value pairs.
:param ont_ani: (dict) ONT-ani to delete
:return: (dict) None if item should be deleted
"""
return None # Implement in your OLT, if needed
def on_vont_ani_create(self, vont_ani):
return vont_ani # Implement in your OLT, if needed
def on_vont_ani_modify(self, vont_ani, update, diffs):
return update # Implement in your OLT, if needed
def on_vont_ani_delete(self, vont_ani):
return None # Implement in your OLT, if needed
def on_venet_create(self, venet):
return venet # Implement in your OLT, if needed
def on_venet_modify(self, venet, update, diffs):
return update # Implement in your OLT, if needed
def on_venet_delete(self, venet):
return None # Implement in your OLT, if needed
def on_tcont_create(self, tcont):
return tcont # Implement in your OLT, if needed
def on_tcont_modify(self, tcont, update, diffs):
return update # Implement in your OLT, if needed
def on_tcont_delete(self, tcont):
return None # Implement in your OLT, if needed
def on_td_create(self, traffic_desc):
return traffic_desc # Implement in your OLT, if needed
def on_td_modify(self, traffic_desc, update, diffs):
return update # Implement in your OLT, if needed
def on_td_delete(self, traffic_desc):
return None # Implement in your OLT, if needed
def on_gemport_create(self, gem_port):
return gem_port # Implement in your OLT, if needed
def on_gemport_modify(self, gem_port, update, diffs):
return update # Implement in your OLT, if needed
def on_gemport_delete(self, gem_port):
return None # Implement in your OLT, if needed
def on_mcast_gemport_create(self, mcast_gem_port):
return mcast_gem_port # Implement in your OLT, if needed
def on_mcast_gemport_modify(self, mcast_gem_port, update, diffs):
return update # Implement in your OLT, if needed
def on_mcast_gemport_delete(self, mcast_gem_port):
return None # Implement in your OLT, if needed
def on_mcast_dist_set_create(self, dist_set):
return dist_set # Implement in your OLT, if needed
def on_mcast_dist_set_modify(self, dist_set, update, diffs):
return update # Implement in your OLT, if needed
def on_mcast_dist_set_delete(self, dist_set):
return None # Implement in your OLT, if needed