blob: babaa7c6a0863df468e66dc43e79c3fad2158996 [file] [log] [blame]
# 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.
#
from Queue import Empty as QueueEmpty
import structlog
import re
from uuid import uuid4
from google.protobuf.empty_pb2 import Empty
from grpc import StatusCode
from voltha.protos.bbf_fiber_base_pb2 import \
AllChannelgroupConfig, ChannelgroupConfig, \
AllChannelpairConfig, ChannelpairConfig, \
AllChannelpartitionConfig, ChannelpartitionConfig, \
AllChannelterminationConfig, ChannelterminationConfig, \
AllOntaniConfig, OntaniConfig, AllVOntaniConfig , VOntaniConfig, \
AllVEnetConfig, VEnetConfig, AllTrafficDescriptorProfileData, \
AllTcontsConfigData, AllGemportsConfigData
from voltha.protos.bbf_fiber_traffic_descriptor_profile_body_pb2 import \
TrafficDescriptorProfileData
from voltha.protos.bbf_fiber_tcont_body_pb2 import TcontsConfigData
from voltha.protos.bbf_fiber_gemport_body_pb2 import GemportsConfigData
from voltha.protos.device_pb2 import Device
from voltha.protos.common_pb2 import AdminState
from common.utils.indexpool import IndexPool
from requests.api import request
log = structlog.get_logger()
class XponHandler(object):
def __init__(self, core):
self.core = core
self.root = None
'''
Pool for handling channel group indices
@TODO: As per current persistency & HA design, each VOLTHA instance
maintains a separate independent database. Since channel-groups are
broadcast to all the VOLTHA instances in the cluster, the
xpon_handler in each instance will independently try to allocate a
unique index. This approach works OK for XGS-PON since CG<->CTerm
relationship is 1:1 for XGS-PON(Since a device can only be served
by one VOLTHA instance and thereby CTerm). This needs to be further
investigated wrt persistency & HA design evolution, for a better
approach in future.
'''
self.cg_pool = IndexPool(2**11-1, 1)
self.cg_dict = {}
def start(self, root):
log.debug('starting xpon_handler')
self.root = root
self.reinitialize_cg_ids()
self.reinitialize_tcont_and_gemport_ids()
def reinitialize_cg_ids(self):
cg_tup = ()
channel_groups = self.root.get('/channel_groups')
for cg in channel_groups:
cg_tup += (cg.cg_index, )
'''
Pools for handling alloc-ids and gemport-ids
@TODO: As per current persistency & HA design, each VOLTHA instance
maintains a separate independent database. Since channel-groups
broadcast to all the VOLTHA instances in the cluster, the
xpon_handler in each instance will independently try to
allocate a unique index. This approach works OK for XGS-PON
since CG<->CTerm relationship is 1:1 for XGS-PON(Since a device
can only be served by one VOLTHA instance and thereby CTerm).
This needs to be further investigated wrt persistency & HA
design evolution, for a better approach in future.
'''
self.cg_dict[cg.name] = {'alloc_id': IndexPool(16383, 1024)}
self.cg_dict[cg.name].update({'gemport_id': IndexPool(64500, 1024)})
self.cg_pool.pre_allocate(cg_tup)
def reinitialize_tcont_and_gemport_ids(self):
tconts = self.root.get('/tconts')
for tc in tconts:
cg_name = self.extract_channel_group_from_request(tc,
'v_ont_anis', tc.interface_reference)
self.cg_dict[cg_name]['alloc_id'].pre_allocate((tc.alloc_id, ))
gemports = self.root.get('/gemports')
for gm in gemports:
cg_name = self.extract_channel_group_from_request(gm,
'v_enets', gm.itf_ref)
self.cg_dict[cg_name]['gemport_id'].pre_allocate((gm.gemport_id, ))
def get_all_channel_group_config(self, request, context):
log.info('grpc-request', request=request)
items = self.root.get('/channel_groups')
return AllChannelgroupConfig(channelgroup_config=items)
def get_channel_group_config(self, request, context):
log.info('grpc-request', request=request)
item = self.root.get('/channel_groups/{}'.format(request.name))
if(isinstance(item, ChannelgroupConfig)):
return item
return Empty()
def create_channel_group(self, request, context):
log.info('grpc-request', request=request)
try:
assert isinstance(request, ChannelgroupConfig), \
'Instance is not of Channel Group'
assert self.validate_interface(request, context)
log.debug('creating-channel-group', name=request.name)
_id = self.cg_pool.get_next()
assert _id != None, \
'Fail to allocate id for Channel Group'
request.cg_index = _id
self.root.add('/channel_groups', request)
self.cg_dict[request.name] = {'alloc_id': IndexPool(16383, 1024)}
self.cg_dict[request.name].update({'gemport_id': IndexPool(64500, 1024)})
return Empty()
except AssertionError, e:
context.set_details(e.message)
context.set_code(StatusCode.INVALID_ARGUMENT)
return Empty()
except ValueError:
self.cg_pool.release(_id)
context.set_details(
'Duplicated channel group \'{}\' cannot be created'.format(
request.name))
context.set_code(StatusCode.INVALID_ARGUMENT)
return Empty()
def update_channel_group(self, request, context):
log.info('grpc-request', request=request)
if '/' in request.name:
context.set_details(
'Malformed name \'{}\''.format(request.name))
context.set_code(StatusCode.INVALID_ARGUMENT)
return ChannelgroupConfig()
try:
assert isinstance(request, ChannelgroupConfig), \
'Instance is not of Channel Group'
assert self.validate_interface(request, context)
channelgroup = self.get_channel_group_config(request, context)
request.cg_index = channelgroup.cg_index
path = '/channel_groups/{}'.format(request.name)
log.debug('updating-channel-group', name=request.name)
self.root.update(path, request, strict=True)
return Empty()
except AssertionError, e:
context.set_details(e.message)
context.set_code(StatusCode.INVALID_ARGUMENT)
return Empty()
except KeyError:
context.set_details(
'channel group \'{}\' not found'.format(request.name))
context.set_code(StatusCode.NOT_FOUND)
return Empty()
def delete_channel_group(self, request, context):
log.info('grpc-request', request=request)
if '/' in request.name:
context.set_details(
'Malformed name \'{}\''.format(request.name))
context.set_code(StatusCode.INVALID_ARGUMENT)
return Empty()
try:
assert isinstance(request, ChannelgroupConfig), \
'Instance is not of Channel Group'
channelgroup_ref_by_channelpartition = next(
(cpart for cpart in self.root.get('/channel_partitions')
if cpart.data.channelgroup_ref == request.name), None)
assert channelgroup_ref_by_channelpartition is None, \
'Channel Group -- \'{}\' is referenced by Channel Partition'\
.format(request.name)
channelgroup_ref_by_channelpair = next(
(cpair for cpair in self.root.get('/channel_pairs')
if cpair.data.channelgroup_ref == request.name), None)
assert channelgroup_ref_by_channelpair is None, \
'Channel Group -- \'{}\' is referenced by Channel Pair'\
.format(request.name)
channelgroup = self.get_channel_group_config(request, context)
path = '/channel_groups/{}'.format(request.name)
log.debug('removing-channel-group', name=request.name)
self.root.remove(path)
self.cg_pool.release(channelgroup.cg_index)
return Empty()
except AssertionError, e:
context.set_details(e.message)
context.set_code(StatusCode.INVALID_ARGUMENT)
return Empty()
except KeyError:
context.set_details(
'channel group \'{}\' not found'.format(request.name))
context.set_code(StatusCode.NOT_FOUND)
return Empty()
def get_all_channel_partition_config(self, request, context):
log.info('grpc-request', request=request)
items = self.root.get('/channel_partitions')
return AllChannelpartitionConfig(channelpartition_config=items)
def create_channel_partition(self, request, context):
log.info('grpc-request', request=request)
try:
assert isinstance(request, ChannelpartitionConfig), \
'Instance is not of Channel Partition'
assert self.validate_interface(request, context)
log.debug('creating-channel-partition', name=request.name)
self.root.add('/channel_partitions', request)
return Empty()
except AssertionError, e:
context.set_details(e.message)
context.set_code(StatusCode.INVALID_ARGUMENT)
return Empty()
except ValueError:
context.set_details(
'Duplicated channel partition \'{}\' cannot be created'.format(
request.name))
context.set_code(StatusCode.INVALID_ARGUMENT)
return Empty()
def update_channel_partition(self, request, context):
log.info('grpc-request', request=request)
if '/' in request.name:
context.set_details(
'Malformed name \'{}\''.format(request.name))
context.set_code(StatusCode.INVALID_ARGUMENT)
return ChannelpartitionConfig()
try:
assert isinstance(request, ChannelpartitionConfig), \
'Instance is not of Channel Partition'
assert self.validate_interface(request, context)
path = '/channel_partitions/{}'.format(request.name)
log.debug('updating-channel-partition', name=request.name)
self.root.update(path, request, strict=True)
return Empty()
except AssertionError, e:
context.set_details(e.message)
context.set_code(StatusCode.INVALID_ARGUMENT)
return Empty()
except KeyError:
context.set_details(
'channel partition \'{}\' not found'.format(request.name))
context.set_code(StatusCode.NOT_FOUND)
return Empty()
def delete_channel_partition(self, request, context):
log.info('grpc-request', request=request)
if '/' in request.name:
context.set_details(
'Malformed name \'{}\''.format(request.name))
context.set_code(StatusCode.INVALID_ARGUMENT)
return Empty()
try:
assert isinstance(request, ChannelpartitionConfig), \
'Instance is not of Channel Partition'
channelpartition_ref_by_channelpair = next(
(cpair for cpair in self.root.get('/channel_pairs')
if cpair.data.channelpartition_ref == request.name), None)
assert channelpartition_ref_by_channelpair is None, \
'Channel Partition -- \'{}\' is referenced by Channel Pair'\
.format(request.name)
channelpartition_ref_by_vontani = next(
(vont_ani for vont_ani in self.root.get('/v_ont_anis')
if vont_ani.data.parent_ref == request.name), None)
assert channelpartition_ref_by_vontani is None, \
'Channel Partition -- \'{}\' is referenced by VOntAni'\
.format(request.name)
path = '/channel_partitions/{}'.format(request.name)
log.debug('removing-channel-partition', name=request.name)
self.root.remove(path)
return Empty()
except AssertionError, e:
context.set_details(e.message)
context.set_code(StatusCode.INVALID_ARGUMENT)
return Empty()
except KeyError:
context.set_details(
'channel partition \'{}\' not found'.format(request.name))
context.set_code(StatusCode.NOT_FOUND)
return Empty()
def get_all_channel_pair_config(self, request, context):
log.info('grpc-request', request=request)
items = self.root.get('/channel_pairs')
return AllChannelpairConfig(channelpair_config=items)
def create_channel_pair(self, request, context):
log.info('grpc-request', request=request)
try:
assert isinstance(request, ChannelpairConfig), \
'Instance is not of Channel Pair'
assert self.validate_interface(request, context)
log.debug('creating-channel-pair', name=request.name)
self.root.add('/channel_pairs', request)
return Empty()
except AssertionError, e:
context.set_details(e.message)
context.set_code(StatusCode.INVALID_ARGUMENT)
return Empty()
except ValueError:
context.set_details(
'Duplicated channel pair \'{}\' cannot be created'.format(
request.name))
context.set_code(StatusCode.INVALID_ARGUMENT)
return Empty()
def update_channel_pair(self, request, context):
log.info('grpc-request', request=request)
if '/' in request.name:
context.set_details(
'Malformed name \'{}\''.format(request.name))
context.set_code(StatusCode.INVALID_ARGUMENT)
return ChannelpairConfig()
try:
assert isinstance(request, ChannelpairConfig), \
'Instance is not of Channel Pair'
assert self.validate_interface(request, context)
path = '/channel_pairs/{}'.format(request.name)
log.debug('updating-channel-pair', name=request.name)
self.root.update(path, request, strict=True)
return Empty()
except AssertionError, e:
context.set_details(e.message)
context.set_code(StatusCode.INVALID_ARGUMENT)
return Empty()
except KeyError:
context.set_details(
'channel pair \'{}\' not found'.format(request.name))
context.set_code(StatusCode.NOT_FOUND)
return Empty()
def delete_channel_pair(self, request, context):
log.info('grpc-request', request=request)
if '/' in request.name:
context.set_details(
'Malformed name \'{}\''.format(request.name))
context.set_code(StatusCode.INVALID_ARGUMENT)
return Empty()
try:
assert isinstance(request, ChannelpairConfig), \
'Instance is not of Channel Pair'
device_items = self.root.get('/devices')
for device_item in device_items:
channelpair_ref_by_channeltermination = next(
(cterm for cterm in self.root.get(
'/devices/{}/channel_terminations'.format(
device_item.id))
if cterm.data.channelpair_ref == request.name), None)
assert channelpair_ref_by_channeltermination is None, \
'Channel Pair -- \'{}\' referenced by Channel Termination'\
.format(request.name)
path = '/channel_pairs/{}'.format(request.name)
log.debug('removing-channel-pair', name=request.name)
self.root.remove(path)
return Empty()
except AssertionError, e:
context.set_details(e.message)
context.set_code(StatusCode.INVALID_ARGUMENT)
return Empty()
except KeyError:
context.set_details(
'channel pair \'{}\' not found'.format(request.name))
context.set_code(StatusCode.NOT_FOUND)
return Empty()
def get_all_channel_termination_config(self, request, context):
log.info('grpc-request', request=request)
if '/' in request.id:
context.set_details(
'Malformed device id \'{}\''.format(request.id))
context.set_code(StatusCode.INVALID_ARGUMENT)
return AllChannelterminationConfig()
try:
items = self.root.get(
'/devices/{}/channel_terminations'.format(request.id))
return AllChannelterminationConfig(channeltermination_config=items)
except KeyError:
context.set_details(
'Device \'{}\' not found'.format(request.id))
context.set_code(StatusCode.NOT_FOUND)
return AllChannelterminationConfig()
def create_channel_termination(self, request, context):
log.info('grpc-request', request=request)
try:
assert isinstance(request, ChannelterminationConfig), \
'Instance is not of Channel Termination'
assert self.validate_interface(request, context)
#device = self.root.get('/devices/{}'.format(request.id))
except AssertionError, e:
context.set_details(e.message)
context.set_code(StatusCode.INVALID_ARGUMENT)
return Empty()
except KeyError:
context.set_details(
'Device \'{}\' not found'.format(request.id))
context.set_code(StatusCode.NOT_FOUND)
return Empty()
try:
path = '/devices/{}/channel_terminations'.format(request.id)
log.debug('creating-channel-termination', name=request.name)
self.root.add(path, request)
return Empty()
except KeyError:
context.set_details(
'Device \'{}\' not activated'.format(request.id))
context.set_code(StatusCode.NOT_FOUND)
return Empty()
except ValueError:
context.set_details(
'Duplicated channel termination \'{}\' cannot be created'.
format(request.name))
context.set_code(StatusCode.INVALID_ARGUMENT)
return Empty()
def update_channel_termination(self, request, context):
log.info('grpc-request', request=request)
if '/' in request.name:
context.set_details(
'Malformed name \'{}\''.format(request.name))
context.set_code(StatusCode.INVALID_ARGUMENT)
return ChannelterminationConfig()
try:
assert isinstance(request, ChannelterminationConfig), \
'Instance is not of Channel Termination'
assert self.validate_interface(request, context)
except AssertionError, e:
context.set_details(e.message)
context.set_code(StatusCode.INVALID_ARGUMENT)
return Empty()
except KeyError:
context.set_details(
'Device \'{}\' not found'.format(request.id))
context.set_code(StatusCode.NOT_FOUND)
return Empty()
try:
path = '/devices/{}/channel_terminations/{}'.format(
request.id, request.name)
log.debug('updating-channel-termination', name=request.name)
self.root.update(path, request, strict=True)
return Empty()
except KeyError:
context.set_details(
'channel termination \'{}\' not found'.format(request.name))
context.set_code(StatusCode.NOT_FOUND)
return Empty()
def delete_channel_termination(self, request, context):
log.info('grpc-request', request=request)
if '/' in request.id:
context.set_details(
'Malformed device id \'{}\''.format(request.id))
context.set_code(StatusCode.INVALID_ARGUMENT)
return Empty()
if '/' in request.name:
context.set_details(
'Malformed name \'{}\''.format(request.name))
context.set_code(StatusCode.INVALID_ARGUMENT)
return Empty()
try:
assert isinstance(request, ChannelterminationConfig)
except AssertionError:
context.set_details(
'Instance is not of Channel Termination')
context.set_code(StatusCode.INVALID_ARGUMENT)
return Empty()
except KeyError:
context.set_details(
'Device \'{}\' not found'.format(request.id))
context.set_code(StatusCode.NOT_FOUND)
return Empty()
try:
path = '/devices/{}/channel_terminations/{}'.format(
request.id, request.name)
except KeyError:
context.set_details(
'channel termination \'{}\' not found'.format(request.name))
context.set_code(StatusCode.NOT_FOUND)
return Empty()
try:
log.debug('removing-channel-termination', name=request.name)
self.root.remove(path)
return Empty()
except KeyError:
context.set_details(
'Could not delete channel termination \'{}\''.format(
request.name))
context.set_code(StatusCode.INVALID_ARGUMENT)
return Empty()
def get_all_ont_ani_config(self, request, context):
log.info('grpc-request', request=request)
items = self.root.get('/ont_anis')
return AllOntaniConfig(ontani_config=items)
def create_ont_ani(self, request, context):
log.info('grpc-request', request=request)
try:
assert isinstance(request, OntaniConfig), \
'Instance is not of Ont Ani'
assert self.validate_interface(request, context)
log.debug('creating-ont-ani', name=request.name)
self.root.add('/ont_anis', request)
return Empty()
except AssertionError, e:
context.set_details(e.message)
context.set_code(StatusCode.INVALID_ARGUMENT)
return Empty()
except KeyError:
context.set_details(
'Cannot create ontani \'{}\''.format(request.name))
context.set_code(StatusCode.NOT_FOUND)
return Empty()
except ValueError:
context.set_details(
'Duplicated ontani \'{}\' cannot be created'.format(
request.name))
context.set_code(StatusCode.INVALID_ARGUMENT)
return Empty()
def update_ont_ani(self, request, context):
log.info('grpc-request', request=request)
if '/' in request.name:
context.set_details(
'Malformed name \'{}\''.format(request.name))
context.set_code(StatusCode.INVALID_ARGUMENT)
return OntaniConfig()
try:
assert isinstance(request, OntaniConfig), \
'Instance is not of Ont Ani'
assert self.validate_interface(request, context)
path = '/ont_anis/{}'.format(request.name)
log.debug('updating-ont-ani', name=request.name)
self.root.update(path, request, strict=True)
return Empty()
except AssertionError, e:
context.set_details(e.message)
context.set_code(StatusCode.INVALID_ARGUMENT)
return Empty()
except KeyError:
context.set_details(
'ontani \'{}\' not found'.format(request.name))
context.set_code(StatusCode.NOT_FOUND)
return Empty()
def delete_ont_ani(self, request, context):
log.info('grpc-request', request=request)
if '/' in request.name:
context.set_details(
'Malformed name \'{}\''.format(request.name))
context.set_code(StatusCode.INVALID_ARGUMENT)
return Empty()
try:
assert isinstance(request, OntaniConfig)
path = '/ont_anis/{}'.format(request.name)
log.debug('removing-ont-ani', name=request.name)
self.root.remove(path)
return Empty()
except AssertionError:
context.set_details(
'Instance is not of Ont Ani')
context.set_code(StatusCode.INVALID_ARGUMENT)
return Empty()
except KeyError:
context.set_details(
'ontani \'{}\' not found'.format(request.name))
context.set_code(StatusCode.NOT_FOUND)
return Empty()
def get_all_v_ont_ani_config(self, request, context):
log.info('grpc-request', request=request)
items = self.root.get('/v_ont_anis')
return AllVOntaniConfig(v_ontani_config=items)
def create_v_ont_ani(self, request, context):
log.info('grpc-request', request=request)
try:
assert isinstance(request, VOntaniConfig), \
'Instance is not of VOnt Ani'
assert self.validate_interface(request, context)
log.debug('creating-vont-ani', name=request.name)
self.root.add('/v_ont_anis', request)
return Empty()
except AssertionError, e:
context.set_details(e.message)
context.set_code(StatusCode.INVALID_ARGUMENT)
return Empty()
except KeyError:
context.set_details(
'Cannot create vontani \'{}\''.format(request.name))
context.set_code(StatusCode.NOT_FOUND)
return Empty()
except ValueError:
context.set_details(
'Duplicated vontani \'{}\' cannot be created'.format(
request.name))
context.set_code(StatusCode.INVALID_ARGUMENT)
return Empty()
def update_v_ont_ani(self, request, context):
log.info('grpc-request', request=request)
if '/' in request.name:
context.set_details(
'Malformed name \'{}\''.format(request.name))
context.set_code(StatusCode.INVALID_ARGUMENT)
return VOntaniConfig()
try:
assert isinstance(request, VOntaniConfig), \
'Instance is not of VOnt Ani'
assert self.validate_interface(request, context)
path = '/v_ont_anis/{}'.format(request.name)
log.debug('updating-vont-ani', name=request.name)
self.root.update(path, request, strict=True)
return Empty()
except AssertionError, e:
context.set_details(e.message)
context.set_code(StatusCode.INVALID_ARGUMENT)
return Empty()
except KeyError:
context.set_details(
'vontani \'{}\' not found'.format(request.name))
context.set_code(StatusCode.NOT_FOUND)
return Empty()
def delete_v_ont_ani(self, request, context):
log.info('grpc-request', request=request)
if '/' in request.name:
context.set_details(
'Malformed name \'{}\''.format(request.name))
context.set_code(StatusCode.INVALID_ARGUMENT)
return Empty()
try:
assert isinstance(request, VOntaniConfig), \
'Instance is not of vont ani'
vontani_ref_by_venet = next(
(venet for venet in self.root.get('/v_enets')
if venet.data.v_ontani_ref == request.name), None)
assert vontani_ref_by_venet is None, \
'VOntAni -- \'{}\' is referenced by VEnet'.format(
request.name)
vontani_ref_by_tcont = next(
(tcont for tcont in self.root.get('/tconts')
if tcont.interface_reference == request.name), None)
assert vontani_ref_by_tcont is None, \
'VOntAni -- \'{}\' is referenced by TCont'.format(
request.name)
path = '/v_ont_anis/{}'.format(request.name)
log.debug('removing-vont-ani', name=request.name)
self.root.remove(path)
return Empty()
except AssertionError, e:
context.set_details(e.message)
context.set_code(StatusCode.INVALID_ARGUMENT)
return Empty()
except KeyError:
context.set_details(
'vontani \'{}\' not found'.format(request.name))
context.set_code(StatusCode.NOT_FOUND)
return Empty()
def get_all_v_enet_config(self, request, context):
log.info('grpc-request', request=request)
items = self.root.get('/v_enets')
return AllVEnetConfig(v_enet_config=items)
def create_v_enet(self, request, context):
log.info('grpc-request', request=request)
try:
assert isinstance(request, VEnetConfig), \
'Instance is not of VEnet'
assert self.validate_interface(request, context)
log.debug('creating-venet', name=request.name)
self.root.add('/v_enets', request)
return Empty()
except AssertionError, e:
context.set_details(e.message)
context.set_code(StatusCode.INVALID_ARGUMENT)
return Empty()
except KeyError:
context.set_details(
'Cannot create venet \'{}\''.format(request.name))
context.set_code(StatusCode.NOT_FOUND)
return Empty()
except ValueError:
context.set_details(
'Duplicated venet \'{}\' cannot be created'.format(
request.name))
context.set_code(StatusCode.INVALID_ARGUMENT)
return Empty()
def update_v_enet(self, request, context):
log.info('grpc-request', request=request)
if '/' in request.name:
context.set_details(
'Malformed name \'{}\''.format(request.name))
context.set_code(StatusCode.INVALID_ARGUMENT)
return VEnetConfig()
try:
assert isinstance(request, VEnetConfig), \
'Instance is not of VEnet'
assert self.validate_interface(request, context)
path = '/v_enets/{}'.format(request.name)
log.debug('updating-venet', name=request.name)
self.root.update(path, request, strict=True)
return Empty()
except AssertionError, e:
context.set_details(e.message)
context.set_code(StatusCode.INVALID_ARGUMENT)
return Empty()
except KeyError:
context.set_details(
'venet \'{}\' not found'.format(request.name))
context.set_code(StatusCode.NOT_FOUND)
return Empty()
def delete_v_enet(self, request, context):
log.info('grpc-request', request=request)
if '/' in request.name:
context.set_details(
'Malformed name \'{}\''.format(request.name))
context.set_code(StatusCode.INVALID_ARGUMENT)
return Empty()
try:
assert isinstance(request, VEnetConfig), \
'Instance is not of VEnet'
#assert device.admin_state == AdminState.DISABLED, \
#'Device to delete cannot be ' \
#'in admin state \'{}\''.format(device.admin_state)
v_enet_ref_by_gemport = next(
(gemport for gemport in self.root.get('/gemports')
if gemport.itf_ref == request.name), None)
assert v_enet_ref_by_gemport is None, \
'The VEnet -- \'{}\' is referenced by Gemport'.format(
request.name)
path = '/v_enets/{}'.format(request.name)
log.debug('removing-venet', name=request.name)
self.root.remove(path)
return Empty()
except AssertionError, e:
context.set_details(e.message)
context.set_code(StatusCode.INVALID_ARGUMENT)
return Empty()
except KeyError:
context.set_details(
'venet \'{}\' not found'.format(request.name))
context.set_code(StatusCode.NOT_FOUND)
return Empty()
def get_all_traffic_descriptor_profile_data(self, request, context):
log.info('grpc-request', request=request)
items = self.root.get('/traffic_descriptor_profiles')
return AllTrafficDescriptorProfileData(
traffic_descriptor_profiles=items)
def create_traffic_descriptor_profile(self, request, context):
log.info('grpc-request', request=request)
try:
assert isinstance(request, TrafficDescriptorProfileData), \
'Instance is not of Traffic Descriptor Profile'
assert self.validate_interface(request, context)
log.debug('creating-traffic-descriptor-profile', name=request.name)
self.root.add('/traffic_descriptor_profiles', request)
return Empty()
except AssertionError, e:
context.set_details(e.message)
context.set_code(StatusCode.INVALID_ARGUMENT)
return Empty()
except KeyError:
context.set_details(
'Cannot create traffic descriptor profile \'{}\''.format(
request.name))
context.set_code(StatusCode.NOT_FOUND)
return Empty()
except ValueError:
context.set_details(
'Duplicated traffic descriptor profile \'{}\' \
cannot be created'.format(request.name))
context.set_code(StatusCode.INVALID_ARGUMENT)
return Empty()
def update_traffic_descriptor_profile(self, request, context):
log.info('grpc-request', request=request)
if '/' in request.name:
context.set_details(
'Malformed name \'{}\''.format(request.name))
context.set_code(StatusCode.INVALID_ARGUMENT)
return TrafficDescriptorProfileData()
try:
assert isinstance(request, TrafficDescriptorProfileData), \
'Instance is not of Traffic Descriptor Profile'
assert self.validate_interface(request, context)
path = '/traffic_descriptor_profiles/{}'.format(request.name)
log.debug('updating-traffic-descriptor-profile',
name=request.name)
self.root.update(path, request, strict=True)
return Empty()
except AssertionError, e:
context.set_details(e.message)
context.set_code(StatusCode.INVALID_ARGUMENT)
return Empty()
except KeyError:
context.set_details(
'traffic descriptor profile \'{}\' not found'.format(
request.name))
context.set_code(StatusCode.NOT_FOUND)
return Empty()
def delete_traffic_descriptor_profile(self, request, context):
log.info('grpc-request', request=request)
if '/' in request.name:
context.set_details(
'Malformed name \'{}\''.format(request.name))
context.set_code(StatusCode.INVALID_ARGUMENT)
return Empty()
try:
assert isinstance(request, TrafficDescriptorProfileData), \
'Instance is not of Traffic Descriptor Profile'
tdp_ref_by_tcont = next(
(tcont for tcont in self.root.get('/tconts') if
tcont.traffic_descriptor_profile_ref == request.name), None)
assert tdp_ref_by_tcont is None, \
'The Traffic Descriptor Profile -- \'{}\' is referenced \
by TCont'.format(request.name)
path = '/traffic_descriptor_profiles/{}'.format(request.name)
log.debug('removing-traffic-descriptor-profile',
name=request.name)
self.root.remove(path)
return Empty()
except AssertionError, e:
context.set_details(e.message)
context.set_code(StatusCode.INVALID_ARGUMENT)
return Empty()
except KeyError:
context.set_details(
'traffic descriptor profile \'{}\' not found'.format(
request.name))
context.set_code(StatusCode.NOT_FOUND)
return Empty()
def get_all_tconts_config_data(self, request, context):
log.info('grpc-request', request=request)
items = self.root.get('/tconts')
return AllTcontsConfigData(tconts_config=items)
def create_tcont(self, request, context):
log.info('grpc-request', request=request)
try:
assert isinstance(request, TcontsConfigData), \
'Instance is not of TCont'
assert self.validate_interface(request, context)
cg_name = self.extract_channel_group_from_request(request,
'v_ont_anis', request.interface_reference)
if request.alloc_id == 0:
_id = self.cg_dict[cg_name]['alloc_id'].get_next()
assert _id is not None, \
'Fail to allocate id for TCont'
request.alloc_id = _id
else:
_id = self.cg_dict[cg_name]['alloc_id'].allocate(request.alloc_id)
assert _id == request.alloc_id, \
'Fail to allocate id for TCont'
log.debug('creating-tcont', name=request.name)
self.root.add('/tconts', request)
return Empty()
except AssertionError, e:
context.set_details(e.message)
context.set_code(StatusCode.INVALID_ARGUMENT)
return Empty()
except KeyError:
context.set_details(
'Cannot create tcont \'{}\''.format(request.name))
context.set_code(StatusCode.NOT_FOUND)
return Empty()
except ValueError:
self.cg_dict[cg_name]['alloc_id'].release(_id)
context.set_details(
'Duplicated tcont \'{}\' cannot be created'.format(
request.name))
context.set_code(StatusCode.INVALID_ARGUMENT)
return Empty()
def update_tcont(self, request, context):
log.info('grpc-request', request=request)
if '/' in request.name:
context.set_details(
'Malformed name \'{}\''.format(request.name))
context.set_code(StatusCode.INVALID_ARGUMENT)
return TcontsConfigData()
try:
assert isinstance(request, TcontsConfigData), \
'Instance is not of TCont'
assert self.validate_interface(request, context)
path = '/tconts/{}'.format(request.name)
tcont = self.root.get(path)
request.alloc_id = tcont.alloc_id
log.debug('updating-tcont', name=request.name)
self.root.update(path, request, strict=True)
return Empty()
except AssertionError, e:
context.set_details(e.message)
context.set_code(StatusCode.INVALID_ARGUMENT)
return Empty()
except KeyError:
context.set_details(
'tcont \'{}\' not found'.format(request.name))
context.set_code(StatusCode.NOT_FOUND)
return Empty()
def delete_tcont(self, request, context):
log.info('grpc-request', request=request)
if '/' in request.name:
context.set_details(
'Malformed name \'{}\''.format(request.name))
context.set_code(StatusCode.INVALID_ARGUMENT)
return Empty()
try:
assert isinstance(request, TcontsConfigData), \
'Instance is not of TCont'
tcont_ref_by_gemport = next(
(gemport for gemport in self.root.get('/gemports')
if gemport.tcont_ref == request.name), None)
assert tcont_ref_by_gemport is None, \
'The Tcont -- \'{}\' is referenced by GemPort'.format(
request.name)
path = '/tconts/{}'.format(request.name)
tcont = self.root.get(path)
cg_name = self.extract_channel_group_from_request(tcont,
'v_ont_anis', tcont.interface_reference)
log.debug('removing-tcont', name=request.name)
self.root.remove(path)
self.cg_dict[cg_name]['alloc_id'].release(tcont.alloc_id)
return Empty()
except AssertionError, e:
context.set_details(e.message)
context.set_code(StatusCode.INVALID_ARGUMENT)
return Empty()
except KeyError:
context.set_details(
'tcont \'{}\' not found'.format(request.name))
context.set_code(StatusCode.NOT_FOUND)
return Empty()
def get_all_gemports_config_data(self, request, context):
log.info('grpc-request', request=request)
items = self.root.get('/gemports')
return AllGemportsConfigData(gemports_config=items)
def create_gem_port(self, request, context):
log.info('grpc-request', request=request)
try:
assert isinstance(request, GemportsConfigData), \
'Instance is not of GemPort'
assert self.validate_interface(request, context)
cg_name = self.extract_channel_group_from_request(request,
'v_enets', request.itf_ref)
if request.gemport_id == 0:
_id = self.cg_dict[cg_name]['gemport_id'].get_next()
assert _id is not None, \
'Fail to allocate id for GemPort'
request.gemport_id = _id
else:
_id = self.cg_dict[cg_name]['gemport_id'].allocate(request.gemport_id)
assert _id == request.gemport_id, \
'Fail to allocate id for GemPort'
log.debug('creating-gemport', name=request.name)
self.root.add('/gemports', request)
return Empty()
except AssertionError, e:
context.set_details(e.message)
context.set_code(StatusCode.INVALID_ARGUMENT)
return Empty()
except KeyError:
context.set_details(
'Cannot create gemport \'{}\''.format(request.name))
context.set_code(StatusCode.NOT_FOUND)
return Empty()
except ValueError:
self.cg_dict[cg_name]['gemport_id'].release(_id)
context.set_details(
'Duplicated gemport \'{}\' cannot be created'.format(
request.name))
context.set_code(StatusCode.INVALID_ARGUMENT)
return Empty()
def update_gem_port(self, request, context):
log.info('grpc-request', request=request)
if '/' in request.name:
context.set_details(
'Malformed name \'{}\''.format(request.name))
context.set_code(StatusCode.INVALID_ARGUMENT)
return GemportsConfigData()
try:
assert isinstance(request, GemportsConfigData), \
'Instance is not of GemPort'
assert self.validate_interface(request, context)
path = '/gemports/{}'.format(request.name)
gemport = self.root.get(path)
request.gemport_id = gemport.gemport_id
log.debug('updating-gemport', name=request.name)
self.root.update(path, request, strict=True)
return Empty()
except AssertionError, e:
context.set_details(e.message)
context.set_code(StatusCode.INVALID_ARGUMENT)
return Empty()
except KeyError:
context.set_details(
'gemport \'{}\' not found'.format(request.name))
context.set_code(StatusCode.NOT_FOUND)
return Empty()
def delete_gem_port(self, request, context):
log.info('grpc-request', request=request)
if '/' in request.name:
context.set_details(
'Malformed name \'{}\''.format(request.name))
context.set_code(StatusCode.INVALID_ARGUMENT)
return Empty()
try:
assert isinstance(request, GemportsConfigData)
path = '/gemports/{}'.format(request.name)
gemport = self.root.get(path)
cg_name = self.extract_channel_group_from_request(gemport,
'v_enets', gemport.itf_ref)
log.debug('removing-gemport', name=request.name)
self.root.remove(path)
self.cg_dict[cg_name]['gemport_id'].release(gemport.gemport_id)
return Empty()
except AssertionError:
context.set_details('Instance is not of GemPort')
context.set_code(StatusCode.INVALID_ARGUMENT)
return Empty()
except KeyError:
context.set_details(
'gemport \'{}\' not found'.format(request.name))
context.set_code(StatusCode.NOT_FOUND)
return Empty()
def validate_interface(self, request, context):
try:
if(isinstance(request, ChannelgroupConfig)):
assert isinstance(request, ChannelgroupConfig)
channelgroup = request
assert channelgroup.name != '', \
'Channel Group name is mandatory'
assert 0 <= channelgroup.data.polling_period <= 864000, \
'Channel Group polling period must be in range of \
[1, 864000]'
return True
elif(isinstance(request, ChannelpartitionConfig)):
assert isinstance(request, ChannelpartitionConfig)
channelpartition = request
assert channelpartition.name != '', \
'Channel Partition name is mandatory'
assert channelpartition.data.channelgroup_ref != '', \
'Channel Partition must reference a channel group'
assert self.get_ref_data(
request, context, "channel_groups",
request.data.channelgroup_ref), \
'Reference to channel group -- \'{}\' not found'.format(
request.data.channelgroup_ref)
assert 0 <= channelpartition.data.closest_ont_distance <= 40, \
'Channel Partition closest ont distance must be in range \
of [0, 40]'
assert channelpartition.data.differential_fiber_distance \
== 0 or \
channelpartition.data.differential_fiber_distance == 20 \
or channelpartition.data.differential_fiber_distance \
== 34 or \
channelpartition.data.differential_fiber_distance == 40, \
'Channel Partition differential fiber distance must be \
[20 | 34 | 40]'
return True
elif(isinstance(request, ChannelpairConfig)):
assert isinstance(request, ChannelpairConfig)
channelpair = request
channelpair_type = ["channelpair", "channelpair_xgs"]
channelpair_speed_type = ["unplanned_cp_speed",
"down_10_up_10", "down_10_up_2_5",
"down_2_5_up_2_5"]
assert channelpair.name != '', 'Channel Pair name is mandatory'
if channelpair.data.channelgroup_ref:
assert self.get_ref_data(
request, context, "channel_groups",
request.data.channelgroup_ref), \
'Reference to channel group -- \'{}\' not found'\
.format(request.data.channelgroup_ref)
'''
@todo: For VOLTHA 1.0, make this reference mandatory until
VOL-314 & VOL-356 are implemented.
'''
assert channelpair.data.channelpartition_ref != '', \
'Channel Pair must reference an existing Channel Partition'
assert self.get_ref_data(
request, context, "channel_partitions",
request.data.channelpartition_ref), \
'Reference to channel partition -- \'{}\' not found'\
.format(request.data.channelpartition_ref)
assert channelpair.data.channelpair_type != '', \
'Channel Pair type is mandatory'
assert channelpair.data.channelpair_type in channelpair_type, \
'Invalid value for Channel Pair type \'{}\''\
.format(channelpair.data.channelpair_type)
assert channelpair.data.channelpair_linerate in \
channelpair_speed_type, \
'Invalid value for Channel Pair linerate \'{}\''\
.format(channelpair.data.channelpair_linerate)
return True
elif(isinstance(request, ChannelterminationConfig)):
assert isinstance(request, ChannelterminationConfig)
channeltermin = request
assert '/' not in channeltermin.id, \
'Malformed device id \'{}\''.format(request.id)
assert channeltermin.id != '', 'Device ID is mandatory'
assert channeltermin.name != '', \
'Channel Termination name is mandatory'
'''
@todo: For VOLTHA 1.0, make this reference mandatory until
VOL-314 & VOL-356 are implemented.
'''
assert channeltermin.data.channelpair_ref != '', \
'Channel Termination must reference Channel Pair'
assert self.get_ref_data(
request, context, "channel_pairs",
request.data.channelpair_ref), \
'Reference to channel pair -- \'{}\' not found'\
.format(request.data.channelpair_ref)
assert 0 <= channeltermin.data.ber_calc_period <= 864000, \
'Channel Termination ber calc period must be in range of \
[1, 864000]'
return True
elif(isinstance(request, OntaniConfig)):
assert isinstance(request, OntaniConfig)
ontani = request
assert ontani.name != '', 'OntAni name is mandatory'
return True
elif(isinstance(request, VOntaniConfig)):
assert isinstance(request, VOntaniConfig)
vontani = request
assert vontani.name != '', 'VOntAni name is mandatory'
assert vontani.data.parent_ref != '', \
'VOntAni must reference an existing Channel Partition'
assert self.get_ref_data(
request, context, "channel_partitions",
request.data.parent_ref), \
'Reference to channel partition -- \'{}\' not found'\
.format(request.data.parent_ref)
'''
@todo: For VOLTHA 1.0, make this reference mandatory until
VOL-314 & VOL-356 are implemented.
'''
assert vontani.data.preferred_chanpair != '', \
'VOntAni must reference preferred Channel Pair'
#if vontani.data.preferred_chanpair:
assert self.get_ref_data(
request, context, "channel_pairs",
request.data.preferred_chanpair), \
'Preferred channel pair -- \'{}\' not found'\
.format(request.data.preferred_chanpair)
if vontani.data.protection_chanpair:
assert self.get_ref_data(
request, context, "channel_pairs",
request.data.protection_chanpair), \
'Protection channel pair -- \'{}\' not found'\
.format(request.data.protection_chanpair)
assert 0 <= len(vontani.data.expected_registration_id) <= 36, \
'VOnt Ani expected registration id string length must be \
in range of [0, 36]'
assert 0 <= vontani.data.onu_id <= 1020, \
'VOnt Ani ONU id must be in range of [0, 1020]'
items = self.root.get('/v_ont_anis')
for item in items:
if item.data.parent_ref == vontani.data.parent_ref or \
item.data.preferred_chanpair == \
vontani.data.preferred_chanpair or \
item.data.protection_chanpair == \
vontani.data.protection_chanpair:
if item.name != vontani.name:
assert item.data.onu_id != vontani.data.onu_id, \
'VOnt Ani ONU id -- \'{}\' already exists, \
but must be unique within channel group'\
.format(vontani.data.onu_id)
return True
elif(isinstance(request, VEnetConfig)):
assert isinstance(request, VEnetConfig)
venet = request
assert venet.name != '', 'VEnet name is mandatory'
assert venet.data.v_ontani_ref != '', \
'VEnet must reference an existing VOntAni'
assert self.get_ref_data(
request, context, "v_ont_anis", venet.data.v_ontani_ref), \
'Reference to VOntAni -- \'{}\' not found'\
.format(venet.data.v_ontani_ref)
return True
elif(isinstance(request, TrafficDescriptorProfileData)):
assert isinstance(request, TrafficDescriptorProfileData)
traffic_descriptor = request
assert traffic_descriptor.name != '', \
'Traffic Descriptor Profile name is mandatory'
assert traffic_descriptor.fixed_bandwidth != '', \
'Fixed bandwidth of Traffic Descriptor is mandatory'
assert traffic_descriptor.assured_bandwidth != '', \
'Assured bandwidth of Traffic Descriptor is mandatory'
assert traffic_descriptor.maximum_bandwidth != '', \
'Maximum bandwidth of Traffic Descriptor is mandatory'
assert 0 <= traffic_descriptor.priority <= 8, \
'Traffic Descriptor Profile priority for or scheduling \
traffic on a TCont must be in range of [1, 8]'
return True
elif(isinstance(request, TcontsConfigData)):
assert isinstance(request, TcontsConfigData)
tcont = request
assert tcont.name != '', 'TCont name is mandatory'
assert tcont.interface_reference != '', \
'TCont must reference a vont ani interface'
assert tcont.traffic_descriptor_profile_ref != '', \
'TCont must reference an existing traffic descriptor \
profile'
assert self.get_ref_data(
request, context, "v_ont_anis",
tcont.interface_reference), \
'Reference to vont ani interface -- \'{}\' not found'\
.format(tcont.interface_reference)
assert self.get_ref_data(
request, context, "traffic_descriptor_profiles",
tcont.traffic_descriptor_profile_ref), \
'Reference to traffic descriptor profile -- \'{}\' \
not found'.format(tcont.traffic_descriptor_profile_ref)
return True
elif(isinstance(request, GemportsConfigData)):
assert isinstance(request, GemportsConfigData)
gemport = request
assert gemport.name != '', 'Gemport name is mandatory'
assert gemport.itf_ref != '', \
'GemPort must reference an existing VEnet interface'
assert self.get_ref_data(
request, context, "v_enets", gemport.itf_ref), \
'Reference to VEnet interface -- \'{}\' not found'\
.format(gemport.itf_ref)
assert 0 <= gemport.traffic_class <= 7, \
'Traffic class value for Gemport \
must be in range of [0, 7]'
'''
@todo: For VOLTHA 1.0, make this reference mandatory until
VOL-314 & VOL-356 are implemented.
'''
assert gemport.tcont_ref != '', \
'GemPort must reference an existing TCont'
assert self.get_ref_data(
request, context, "tconts", gemport.tcont_ref), \
'Reference to tcont -- \'{}\' not found'\
.format(gemport.tcont_ref)
return True
else:
return False
except AssertionError, e:
context.set_details(e.message)
context.set_code(StatusCode.INVALID_ARGUMENT)
return False
def get_ref_data(self, request, context, interface, reference):
depth = int(dict(context.invocation_metadata()).get('get-depth', 0))
try:
path = '/{}/'.format(interface)
self.root.get(path + reference, depth=depth)
log.info('reference-for-{}-found-\'{}\''\
.format(interface, reference))
return True
except KeyError:
log.info('reference-for-{}-not-found-\'{}\''\
.format(interface, reference))
return False
def extract_channel_group_from_request(self, request, interface,
reference):
try:
path = '/{}/{}'.format(interface, reference)
item = self.root.get(path)
if isinstance(item, ChannelgroupConfig):
return item.name
elif isinstance(item, VEnetConfig):
return self.extract_channel_group_from_request(Empty(),
'v_ont_anis', item.data.v_ontani_ref)
elif isinstance(item, VOntaniConfig):
return self.extract_channel_group_from_request(Empty(),
'channel_partitions', item.data.parent_ref)
elif isinstance(item, ChannelpartitionConfig):
return self.extract_channel_group_from_request(Empty(),
'channel_groups', item.data.channelgroup_ref)
except KeyError:
log.info('reference-for-{}-not found'.format(interface))
return Empty()