Skip to content
Snippets Groups Projects
Commit 39b9f3f9 authored by Lluis Gifre Renom's avatar Lluis Gifre Renom
Browse files

Several changes:

- Initial (unfinished) version of Slice component
- Factorized and improved check methods from "device" and "service" components to align with "slice" component
parent f2287700
No related branches found
No related tags found
1 merge request!54Release 2.0.0
#!/bin/bash -eu
#
# Copyright 2018 Google LLC
#
# 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.
#!/bin/bash -e
# Make folder containing the script the root folder for its execution
cd $(dirname $0)
rm -rf proto/*.py
rm -rf proto/__pycache__
touch proto/__init__.py
python -m grpc_tools.protoc -I../../proto --python_out=proto --grpc_python_out=proto context.proto
python -m grpc_tools.protoc -I../../proto --python_out=proto --grpc_python_out=proto service.proto
python -m grpc_tools.protoc -I../../proto --python_out=proto --grpc_python_out=proto slice.proto
rm proto/context_pb2_grpc.py
rm proto/service_pb2_grpc.py
sed -i -E 's/(import\ .*)_pb2/from . \1_pb2/g' proto/context_pb2.py
sed -i -E 's/(import\ .*)_pb2/from . \1_pb2/g' proto/service_pb2.py
sed -i -E 's/(import\ .*)_pb2/from . \1_pb2/g' proto/slice_pb2.py
sed -i -E 's/(import\ .*)_pb2/from . \1_pb2/g' proto/slice_pb2_grpc.py
This diff is collapsed.
This diff is collapsed.
# -*- coding: utf-8 -*-
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: slice.proto
"""Generated protocol buffer code."""
from google.protobuf.internal import enum_type_wrapper
from google.protobuf import descriptor as _descriptor
from google.protobuf import message as _message
from google.protobuf import reflection as _reflection
from google.protobuf import symbol_database as _symbol_database
# @@protoc_insertion_point(imports)
_sym_db = _symbol_database.Default()
from . import context_pb2 as context__pb2
from . import service_pb2 as service__pb2
DESCRIPTOR = _descriptor.FileDescriptor(
name='slice.proto',
package='slice',
syntax='proto3',
serialized_options=None,
create_key=_descriptor._internal_create_key,
serialized_pb=b'\n\x0bslice.proto\x12\x05slice\x1a\rcontext.proto\x1a\rservice.proto\"3\n\rSliceEndpoint\x12\"\n\x07port_id\x18\x01 \x01(\x0b\x32\x11.context.EndPoint\"\xf4\x01\n\x0eTransportSlice\x12 \n\x08slice_id\x18\x01 \x01(\x0b\x32\x0e.slice.SliceId\x12\'\n\tendpoints\x18\x02 \x03(\x0b\x32\x14.slice.SliceEndpoint\x12(\n\x0b\x63onstraints\x18\x03 \x03(\x0b\x32\x13.context.Constraint\x12$\n\x08services\x18\x04 \x03(\x0b\x32\x12.service.ServiceId\x12#\n\x0bsubSlicesId\x18\x05 \x03(\x0b\x32\x0e.slice.SliceId\x12\"\n\x06status\x18\x06 \x01(\x0b\x32\x12.slice.SliceStatus\"Q\n\x07SliceId\x12%\n\tcontextId\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x1f\n\x08slice_id\x18\x02 \x01(\x0b\x32\r.context.Uuid\"W\n\x0bSliceStatus\x12 \n\x08slice_id\x18\x01 \x01(\x0b\x32\x0e.slice.SliceId\x12&\n\x06status\x18\x02 \x01(\x0e\x32\x16.slice.SliceStatusEnum*@\n\x0fSliceStatusEnum\x12\x0b\n\x07PLANNED\x10\x00\x12\x08\n\x04INIT\x10\x01\x12\n\n\x06\x41\x43TIVE\x10\x02\x12\n\n\x06\x44\x45INIT\x10\x03\x32\x88\x01\n\x0cSliceService\x12@\n\x11\x43reateUpdateSlice\x12\x15.slice.TransportSlice\x1a\x12.slice.SliceStatus\"\x00\x12\x36\n\x0b\x44\x65leteSlice\x12\x15.slice.TransportSlice\x1a\x0e.context.Empty\"\x00\x62\x06proto3'
,
dependencies=[context__pb2.DESCRIPTOR,service__pb2.DESCRIPTOR,])
_SLICESTATUSENUM = _descriptor.EnumDescriptor(
name='SliceStatusEnum',
full_name='slice.SliceStatusEnum',
filename=None,
file=DESCRIPTOR,
create_key=_descriptor._internal_create_key,
values=[
_descriptor.EnumValueDescriptor(
name='PLANNED', index=0, number=0,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name='INIT', index=1, number=1,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name='ACTIVE', index=2, number=2,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name='DEINIT', index=3, number=3,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
],
containing_type=None,
serialized_options=None,
serialized_start=524,
serialized_end=588,
)
_sym_db.RegisterEnumDescriptor(_SLICESTATUSENUM)
SliceStatusEnum = enum_type_wrapper.EnumTypeWrapper(_SLICESTATUSENUM)
PLANNED = 0
INIT = 1
ACTIVE = 2
DEINIT = 3
_SLICEENDPOINT = _descriptor.Descriptor(
name='SliceEndpoint',
full_name='slice.SliceEndpoint',
filename=None,
file=DESCRIPTOR,
containing_type=None,
create_key=_descriptor._internal_create_key,
fields=[
_descriptor.FieldDescriptor(
name='port_id', full_name='slice.SliceEndpoint.port_id', index=0,
number=1, type=11, cpp_type=10, label=1,
has_default_value=False, default_value=None,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
],
extensions=[
],
nested_types=[],
enum_types=[
],
serialized_options=None,
is_extendable=False,
syntax='proto3',
extension_ranges=[],
oneofs=[
],
serialized_start=52,
serialized_end=103,
)
_TRANSPORTSLICE = _descriptor.Descriptor(
name='TransportSlice',
full_name='slice.TransportSlice',
filename=None,
file=DESCRIPTOR,
containing_type=None,
create_key=_descriptor._internal_create_key,
fields=[
_descriptor.FieldDescriptor(
name='slice_id', full_name='slice.TransportSlice.slice_id', index=0,
number=1, type=11, cpp_type=10, label=1,
has_default_value=False, default_value=None,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
_descriptor.FieldDescriptor(
name='endpoints', full_name='slice.TransportSlice.endpoints', index=1,
number=2, type=11, cpp_type=10, label=3,
has_default_value=False, default_value=[],
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
_descriptor.FieldDescriptor(
name='constraints', full_name='slice.TransportSlice.constraints', index=2,
number=3, type=11, cpp_type=10, label=3,
has_default_value=False, default_value=[],
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
_descriptor.FieldDescriptor(
name='services', full_name='slice.TransportSlice.services', index=3,
number=4, type=11, cpp_type=10, label=3,
has_default_value=False, default_value=[],
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
_descriptor.FieldDescriptor(
name='subSlicesId', full_name='slice.TransportSlice.subSlicesId', index=4,
number=5, type=11, cpp_type=10, label=3,
has_default_value=False, default_value=[],
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
_descriptor.FieldDescriptor(
name='status', full_name='slice.TransportSlice.status', index=5,
number=6, type=11, cpp_type=10, label=1,
has_default_value=False, default_value=None,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
],
extensions=[
],
nested_types=[],
enum_types=[
],
serialized_options=None,
is_extendable=False,
syntax='proto3',
extension_ranges=[],
oneofs=[
],
serialized_start=106,
serialized_end=350,
)
_SLICEID = _descriptor.Descriptor(
name='SliceId',
full_name='slice.SliceId',
filename=None,
file=DESCRIPTOR,
containing_type=None,
create_key=_descriptor._internal_create_key,
fields=[
_descriptor.FieldDescriptor(
name='contextId', full_name='slice.SliceId.contextId', index=0,
number=1, type=11, cpp_type=10, label=1,
has_default_value=False, default_value=None,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
_descriptor.FieldDescriptor(
name='slice_id', full_name='slice.SliceId.slice_id', index=1,
number=2, type=11, cpp_type=10, label=1,
has_default_value=False, default_value=None,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
],
extensions=[
],
nested_types=[],
enum_types=[
],
serialized_options=None,
is_extendable=False,
syntax='proto3',
extension_ranges=[],
oneofs=[
],
serialized_start=352,
serialized_end=433,
)
_SLICESTATUS = _descriptor.Descriptor(
name='SliceStatus',
full_name='slice.SliceStatus',
filename=None,
file=DESCRIPTOR,
containing_type=None,
create_key=_descriptor._internal_create_key,
fields=[
_descriptor.FieldDescriptor(
name='slice_id', full_name='slice.SliceStatus.slice_id', index=0,
number=1, type=11, cpp_type=10, label=1,
has_default_value=False, default_value=None,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
_descriptor.FieldDescriptor(
name='status', full_name='slice.SliceStatus.status', index=1,
number=2, type=14, cpp_type=8, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
],
extensions=[
],
nested_types=[],
enum_types=[
],
serialized_options=None,
is_extendable=False,
syntax='proto3',
extension_ranges=[],
oneofs=[
],
serialized_start=435,
serialized_end=522,
)
_SLICEENDPOINT.fields_by_name['port_id'].message_type = context__pb2._ENDPOINT
_TRANSPORTSLICE.fields_by_name['slice_id'].message_type = _SLICEID
_TRANSPORTSLICE.fields_by_name['endpoints'].message_type = _SLICEENDPOINT
_TRANSPORTSLICE.fields_by_name['constraints'].message_type = context__pb2._CONSTRAINT
_TRANSPORTSLICE.fields_by_name['services'].message_type = service__pb2._SERVICEID
_TRANSPORTSLICE.fields_by_name['subSlicesId'].message_type = _SLICEID
_TRANSPORTSLICE.fields_by_name['status'].message_type = _SLICESTATUS
_SLICEID.fields_by_name['contextId'].message_type = context__pb2._CONTEXTID
_SLICEID.fields_by_name['slice_id'].message_type = context__pb2._UUID
_SLICESTATUS.fields_by_name['slice_id'].message_type = _SLICEID
_SLICESTATUS.fields_by_name['status'].enum_type = _SLICESTATUSENUM
DESCRIPTOR.message_types_by_name['SliceEndpoint'] = _SLICEENDPOINT
DESCRIPTOR.message_types_by_name['TransportSlice'] = _TRANSPORTSLICE
DESCRIPTOR.message_types_by_name['SliceId'] = _SLICEID
DESCRIPTOR.message_types_by_name['SliceStatus'] = _SLICESTATUS
DESCRIPTOR.enum_types_by_name['SliceStatusEnum'] = _SLICESTATUSENUM
_sym_db.RegisterFileDescriptor(DESCRIPTOR)
SliceEndpoint = _reflection.GeneratedProtocolMessageType('SliceEndpoint', (_message.Message,), {
'DESCRIPTOR' : _SLICEENDPOINT,
'__module__' : 'slice_pb2'
# @@protoc_insertion_point(class_scope:slice.SliceEndpoint)
})
_sym_db.RegisterMessage(SliceEndpoint)
TransportSlice = _reflection.GeneratedProtocolMessageType('TransportSlice', (_message.Message,), {
'DESCRIPTOR' : _TRANSPORTSLICE,
'__module__' : 'slice_pb2'
# @@protoc_insertion_point(class_scope:slice.TransportSlice)
})
_sym_db.RegisterMessage(TransportSlice)
SliceId = _reflection.GeneratedProtocolMessageType('SliceId', (_message.Message,), {
'DESCRIPTOR' : _SLICEID,
'__module__' : 'slice_pb2'
# @@protoc_insertion_point(class_scope:slice.SliceId)
})
_sym_db.RegisterMessage(SliceId)
SliceStatus = _reflection.GeneratedProtocolMessageType('SliceStatus', (_message.Message,), {
'DESCRIPTOR' : _SLICESTATUS,
'__module__' : 'slice_pb2'
# @@protoc_insertion_point(class_scope:slice.SliceStatus)
})
_sym_db.RegisterMessage(SliceStatus)
_SLICESERVICE = _descriptor.ServiceDescriptor(
name='SliceService',
full_name='slice.SliceService',
file=DESCRIPTOR,
index=0,
serialized_options=None,
create_key=_descriptor._internal_create_key,
serialized_start=591,
serialized_end=727,
methods=[
_descriptor.MethodDescriptor(
name='CreateUpdateSlice',
full_name='slice.SliceService.CreateUpdateSlice',
index=0,
containing_service=None,
input_type=_TRANSPORTSLICE,
output_type=_SLICESTATUS,
serialized_options=None,
create_key=_descriptor._internal_create_key,
),
_descriptor.MethodDescriptor(
name='DeleteSlice',
full_name='slice.SliceService.DeleteSlice',
index=1,
containing_service=None,
input_type=_TRANSPORTSLICE,
output_type=context__pb2._EMPTY,
serialized_options=None,
create_key=_descriptor._internal_create_key,
),
])
_sym_db.RegisterServiceDescriptor(_SLICESERVICE)
DESCRIPTOR.services_by_name['SliceService'] = _SLICESERVICE
# @@protoc_insertion_point(module_scope)
# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
"""Client and server classes corresponding to protobuf-defined services."""
import grpc
from . import context_pb2 as context__pb2
from . import slice_pb2 as slice__pb2
class SliceServiceStub(object):
"""Missing associated documentation comment in .proto file."""
def __init__(self, channel):
"""Constructor.
Args:
channel: A grpc.Channel.
"""
self.CreateUpdateSlice = channel.unary_unary(
'/slice.SliceService/CreateUpdateSlice',
request_serializer=slice__pb2.TransportSlice.SerializeToString,
response_deserializer=slice__pb2.SliceStatus.FromString,
)
self.DeleteSlice = channel.unary_unary(
'/slice.SliceService/DeleteSlice',
request_serializer=slice__pb2.TransportSlice.SerializeToString,
response_deserializer=context__pb2.Empty.FromString,
)
class SliceServiceServicer(object):
"""Missing associated documentation comment in .proto file."""
def CreateUpdateSlice(self, request, context):
"""Missing associated documentation comment in .proto file."""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def DeleteSlice(self, request, context):
"""Missing associated documentation comment in .proto file."""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def add_SliceServiceServicer_to_server(servicer, server):
rpc_method_handlers = {
'CreateUpdateSlice': grpc.unary_unary_rpc_method_handler(
servicer.CreateUpdateSlice,
request_deserializer=slice__pb2.TransportSlice.FromString,
response_serializer=slice__pb2.SliceStatus.SerializeToString,
),
'DeleteSlice': grpc.unary_unary_rpc_method_handler(
servicer.DeleteSlice,
request_deserializer=slice__pb2.TransportSlice.FromString,
response_serializer=context__pb2.Empty.SerializeToString,
),
}
generic_handler = grpc.method_handlers_generic_handler(
'slice.SliceService', rpc_method_handlers)
server.add_generic_rpc_handlers((generic_handler,))
# This class is part of an EXPERIMENTAL API.
class SliceService(object):
"""Missing associated documentation comment in .proto file."""
@staticmethod
def CreateUpdateSlice(request,
target,
options=(),
channel_credentials=None,
call_credentials=None,
insecure=False,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_unary(request, target, '/slice.SliceService/CreateUpdateSlice',
slice__pb2.TransportSlice.SerializeToString,
slice__pb2.SliceStatus.FromString,
options, channel_credentials,
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
@staticmethod
def DeleteSlice(request,
target,
options=(),
channel_credentials=None,
call_credentials=None,
insecure=False,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_unary(request, target, '/slice.SliceService/DeleteSlice',
slice__pb2.TransportSlice.SerializeToString,
context__pb2.Empty.FromString,
options, channel_credentials,
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
grpcio-health-checking
grpcio
prometheus-client
pytest
pytest-benchmark
redis
import grpc
import logging
from concurrent import futures
from grpc_health.v1.health import HealthServicer, OVERALL_HEALTH
from grpc_health.v1.health_pb2 import HealthCheckResponse
from grpc_health.v1.health_pb2_grpc import add_HealthServicer_to_server
from slice.proto.slice_pb2_grpc import add_SliceServiceServicer_to_server
from slice.service.SliceServiceServicerImpl import SliceServiceServicerImpl
from slice.Config import GRPC_SERVICE_PORT, GRPC_MAX_WORKERS, GRPC_GRACE_PERIOD
BIND_ADDRESS = '0.0.0.0'
LOGGER = logging.getLogger(__name__)
class SliceService:
def __init__(self, database, address=BIND_ADDRESS, port=GRPC_SERVICE_PORT, max_workers=GRPC_MAX_WORKERS,
grace_period=GRPC_GRACE_PERIOD):
self.database = database
self.address = address
self.port = port
self.endpoint = None
self.max_workers = max_workers
self.grace_period = grace_period
self.slice_servicer = None
self.health_servicer = None
self.pool = None
self.server = None
def start(self):
self.endpoint = '{}:{}'.format(self.address, self.port)
LOGGER.debug('Starting Service (tentative endpoint: {}, max_workers: {})...'.format(
self.endpoint, self.max_workers))
self.pool = futures.ThreadPoolExecutor(max_workers=self.max_workers)
self.server = grpc.server(self.pool) # , interceptors=(tracer_interceptor,))
self.slice_servicer = SliceServiceServicerImpl(self.database)
add_SliceServiceServicer_to_server(self.slice_servicer, self.server)
self.health_servicer = HealthServicer(
experimental_non_blocking=True, experimental_thread_pool=futures.ThreadPoolExecutor(max_workers=1))
add_HealthServicer_to_server(self.health_servicer, self.server)
port = self.server.add_insecure_port(self.endpoint)
self.endpoint = '{}:{}'.format(self.address, port)
LOGGER.info('Listening on {}...'.format(self.endpoint))
self.server.start()
self.health_servicer.set(OVERALL_HEALTH, HealthCheckResponse.SERVING) # pylint: disable=maybe-no-member
LOGGER.debug('Service started')
def stop(self):
LOGGER.debug('Stopping service (grace period {} seconds)...'.format(self.grace_period))
self.health_servicer.enter_graceful_shutdown()
self.server.stop(self.grace_period)
LOGGER.debug('Service stopped')
import grpc, logging
from prometheus_client import Counter, Histogram
from common.database.api.context.Constants import DEFAULT_CONTEXT_ID, DEFAULT_TOPOLOGY_ID
from common.database.api.Database import Database
from common.exceptions.ServiceException import ServiceException
from slice.proto.context_pb2 import Empty
from slice.proto.slice_pb2 import SliceStatus, TransportSlice
from slice.proto.slice_pb2_grpc import SliceServiceServicer
LOGGER = logging.getLogger(__name__)
CREATEUPDATESLICE_COUNTER_STARTED = Counter ('slice_createupdateslice_counter_started',
'Slice:CreateUpdateSlice counter of requests started' )
CREATEUPDATESLICE_COUNTER_COMPLETED = Counter ('slice_createupdateslice_counter_completed',
'Slice:CreateUpdateSlice counter of requests completed')
CREATEUPDATESLICE_COUNTER_FAILED = Counter ('slice_createupdateslice_counter_failed',
'Slice:CreateUpdateSlice counter of requests failed' )
CREATEUPDATESLICE_HISTOGRAM_DURATION = Histogram('slice_createupdateslice_histogram_duration',
'Slice:CreateUpdateSlice histogram of request duration')
DELETESLICE_COUNTER_STARTED = Counter ('slice_DeleteSlice_counter_started',
'Slice:DeleteSlice counter of requests started' )
DELETESLICE_COUNTER_COMPLETED = Counter ('slice_DeleteSlice_counter_completed',
'Slice:DeleteSlice counter of requests completed')
DELETESLICE_COUNTER_FAILED = Counter ('slice_DeleteSlice_counter_failed',
'Slice:DeleteSlice counter of requests failed' )
DELETESLICE_HISTOGRAM_DURATION = Histogram('slice_DeleteSlice_histogram_duration',
'Slice:DeleteSlice histogram of request duration')
class SliceServiceServicerImpl(SliceServiceServicer):
def __init__(self, database : Database):
LOGGER.debug('Creating Servicer...')
self.database = database
LOGGER.debug('Servicer Created')
@CREATEUPDATESLICE_HISTOGRAM_DURATION.time()
def CreateUpdateSlice(self, request : TransportSlice, grpc_context : grpc.ServicerContext) -> SliceStatus:
CREATEUPDATESLICE_COUNTER_STARTED.inc()
try:
LOGGER.debug('CreateUpdateSlice request: {}'.format(str(request)))
# ----- Validate request data and pre-conditions -----------------------------------------------------------
device_id, device_type, device_config, device_opstat, db_endpoints_ports = \
check_slice_request('CreateUpdateSlice', request, self.database, LOGGER)
# ----- Implement changes in the database ------------------------------------------------------------------
db_context = self.database.context(DEFAULT_CONTEXT_ID).create()
db_topology = db_context.topology(DEFAULT_TOPOLOGY_ID).create()
db_device = db_topology.device(device_id).create(device_type, device_config, device_opstat)
for db_endpoint,port_type in db_endpoints_ports:
db_endpoint.create(port_type)
# ----- Compose reply --------------------------------------------------------------------------------------
reply = SliceStatus(**db_device.dump_id())
LOGGER.debug('CreateUpdateSlice reply: {}'.format(str(reply)))
CREATEUPDATESLICE_COUNTER_COMPLETED.inc()
return reply
except ServiceException as e:
LOGGER.exception('CreateUpdateSlice exception')
CREATEUPDATESLICE_COUNTER_FAILED.inc()
grpc_context.abort(e.code, e.details)
except Exception as e: # pragma: no cover
LOGGER.exception('CreateUpdateSlice exception')
CREATEUPDATESLICE_COUNTER_FAILED.inc()
grpc_context.abort(grpc.StatusCode.INTERNAL, str(e))
@DELETESLICE_HISTOGRAM_DURATION.time()
def DeleteSlice(self, request : TransportSlice, grpc_context : grpc.ServicerContext) -> Empty:
DELETESLICE_COUNTER_STARTED.inc()
try:
LOGGER.debug('DeleteSlice request: {}'.format(str(request)))
# ----- Validate request data and pre-conditions -----------------------------------------------------------
device_id = check_slice_id_request('DeleteSlice', request, self.database, LOGGER)
# ----- Implement changes in the database ------------------------------------------------------------------
db_context = self.database.context(DEFAULT_CONTEXT_ID).create()
db_topology = db_context.topology(DEFAULT_TOPOLOGY_ID).create()
db_topology.device(device_id).delete()
# ----- Compose reply --------------------------------------------------------------------------------------
reply = Empty()
LOGGER.debug('DeleteSlice reply: {}'.format(str(reply)))
DELETESLICE_COUNTER_COMPLETED.inc()
return reply
except ServiceException as e:
LOGGER.exception('DeleteSlice exception')
DELETESLICE_COUNTER_FAILED.inc()
grpc_context.abort(e.code, e.details)
except Exception as e: # pragma: no cover
LOGGER.exception('DeleteSlice exception')
DELETESLICE_COUNTER_FAILED.inc()
grpc_context.abort(grpc.StatusCode.INTERNAL, str(e))
import grpc, logging
from typing import Dict, List, Set, Tuple
from common.Checkers import chk_options, chk_string
from common.database.api.Database import Database
from common.database.api.context.Constants import DEFAULT_CONTEXT_ID, DEFAULT_TOPOLOGY_ID
from common.database.api.context.service.Service import Service
from common.database.api.context.slice.SliceStatus import SliceStatus, slicestatus_enum_values, to_slicestatus_enum
from common.database.api.context.topology.device.Endpoint import Endpoint
from common.exceptions.ServiceException import ServiceException
from common.tools.service.ConstraintsChecker import check_constraints
from common.tools.service.EndpointIdCheckers import check_endpoint_id
from common.tools.service.DeviceCheckers import check_device_endpoint_exists
from common.tools.service.EnumCheckers import check_enum
from common.tools.service.ServiceCheckers import check_service_exists
from common.tools.service.SliceCheckers import check_slice_exists #, check_slice_not_exists
from slice.proto.slice_pb2 import TransportSlice
# For each method name, define acceptable slice statuses. Empty set means accept all.
ACCEPTED_SLICE_STATUSES : Dict[str, Set[SliceStatus]] = {
'CreateUpdateSlice': set([SliceStatus.PLANNED, SliceStatus.INIT, SliceStatus.ACTIVE]),
'DeleteSlice': set([SliceStatus.PLANNED, SliceStatus.DEINIT]),
}
def _check_slice_exists(method_name : str, database : Database, context_id : str, slice_id : str):
if method_name in ['CreateUpdateSlice']:
# Do nothing; creation implies checking slice does not exist. However, if it exists, we can perform an update.
#check_slice_not_exists(database, context_id, slice_id)
pass
elif method_name in ['DeleteSlice']:
check_slice_exists(database, context_id, slice_id)
else: # pragma: no cover (test requires malforming the code)
msg = 'Unexpected condition [_check_slice_exists(method_name={}, slice_id={})]'
msg = msg.format(str(method_name), str(slice_id))
raise ServiceException(grpc.StatusCode.UNIMPLEMENTED, msg)
def _check_slice_endpoints(
logger : logging.Logger, database : Database, context_id : str, slice_id : str, slice_endpoints
) -> List[Tuple[Endpoint, str]]:
add_topology_devices_endpoints : Dict[str, Dict[str, Set[str]]] = {}
db_endpoints__port_types : List[Tuple[Endpoint, str]] = []
for endpoint_number,slice_endpoint in enumerate(slice_endpoints):
parent_name = 'SliceEndpoint(#{}) of Context({})/Slice({})'
parent_name = parent_name.format(endpoint_number, context_id, slice_id)
ep_topology_id, ep_device_id, ep_port_id = check_endpoint_id(
logger, endpoint_number, parent_name, slice_endpoint.port_id.port_id, add_topology_devices_endpoints,
acceptable_context_ids=set([context_id]), prevent_same_device_multiple_times=False)
try:
ep_port_type = chk_string('endpoint[#{}].port_type'.format(endpoint_number),
slice_endpoint.port_id.port_type,
allow_empty=False)
except Exception as e:
logger.exception('Invalid arguments:')
raise ServiceException(grpc.StatusCode.INVALID_ARGUMENT, str(e))
db_endpoint = check_device_endpoint_exists(
database, parent_name, context_id, ep_topology_id, ep_device_id, ep_port_id)
db_endpoints__port_types.append((db_endpoint, ep_port_type))
return db_endpoints__port_types
def _check_services(
logger : logging.Logger, database : Database, parent_name : str, context_id : str, slice_service_ids
) -> List[Service]:
add_context_services : Dict[str, Set[str]] = {}
db_services : List[Service] = []
for service_number,service_id in enumerate(slice_service_ids):
# ----- Parse attributes ---------------------------------------------------------------------------------------
try:
service_context_id = chk_string ('services[#{}].contextId.contextUuid.uuid'.format(service_number),
service_id.contextId.contextUuid.uuid,
allow_empty=True)
service_id = chk_string ('services[#{}].cs_id.uuid'.format(service_number),
service_id.cs_id.uuid,
allow_empty=False)
except Exception as e:
logger.exception('Invalid arguments:')
raise ServiceException(grpc.StatusCode.INVALID_ARGUMENT, str(e))
if len(service_context_id) == 0: service_context_id = context_id
add_services = add_context_services.setdefault(context_id, dict())
if service_id in add_services:
msg = 'Duplicated Context({})/Service({}) in {}.'
msg = msg.format(service_context_id, service_id, parent_name)
raise ServiceException(grpc.StatusCode.INVALID_ARGUMENT, msg)
db_service = check_service_exists(database, service_context_id, service_id)
db_services.append(db_service)
add_services.add(service_id)
return db_services
def _check_subslices(
logger : logging.Logger, database : Database, parent_name : str, context_id : str, slice_subslice_ids
) -> List[Slice]:
add_context_subslices : Dict[str, Set[str]] = {}
db_subslices : List[Slice] = []
for subslice_number,subslice_id in enumerate(slice_subslice_ids):
# ----- Parse attributes ---------------------------------------------------------------------------------------
try:
subslice_context_id = chk_string ('subSlicesId[#{}].contextId.contextUuid.uuid'.format(subslice_number),
subslice_id.contextId.contextUuid.uuid,
allow_empty=True)
subslice_id = chk_string ('subSlicesId[#{}].slice_id.uuid'.format(subslice_number),
subslice_id.slice_id.uuid,
allow_empty=False)
except Exception as e:
logger.exception('Invalid arguments:')
raise ServiceException(grpc.StatusCode.INVALID_ARGUMENT, str(e))
if len(subslice_context_id) == 0: subslice_context_id = context_id
add_subslices = add_context_subslices.setdefault(context_id, dict())
if subslice_id in add_subslices:
msg = 'Duplicated Context({})/Slice({}) in {}.'
msg = msg.format(subslice_context_id, subslice_id, parent_name)
raise ServiceException(grpc.StatusCode.INVALID_ARGUMENT, msg)
db_subslice = check_slice_exists(database, subslice_context_id, subslice_id)
db_subslices.append(db_subslice)
add_subslices.add(subslice_id)
return db_subslices
def check_slice_status(method_name : str, value : str) -> SliceStatus:
return check_enum(
'SliceStatus', method_name, value, to_slicestatus_enum, ACCEPTED_SLICE_STATUSES)
def check_slice_request(
method_name : str, request : TransportSlice, database : Database, logger : logging.Logger
): # -> Tuple[str, str, str, OperationalStatus, List[Tuple[Endpoint, str]]]:
# ----- Parse attributes -------------------------------------------------------------------------------------------
try:
context_id = chk_string ('slice.slice_id.contextId.contextUuid.uuid',
request.slice_id.contextId.contextUuid.uuid,
allow_empty=True)
slice_id = chk_string ('slice.slice_id.slice_id.uuid',
request.slice_id.slice_id.uuid,
allow_empty=False)
status_context_id = chk_string ('slice.status.slice_id.contextId.contextUuid.uuid',
request.status.slice_id.contextId.contextUuid.uuid,
allow_empty=True)
status_slice_id = chk_string ('slice.status.slice_id.slice_id.uuid',
request.status.slice_id.slice_id.uuid,
allow_empty=True)
slice_status = chk_options('slice.status.status',
request.status.status,
slicestatus_enum_values())
except Exception as e:
logger.exception('Invalid arguments:')
raise ServiceException(grpc.StatusCode.INVALID_ARGUMENT, str(e))
if len(context_id) == 0: context_id = DEFAULT_CONTEXT_ID
if (len(status_context_id) > 0) and (status_context_id != context_id):
msg = ' '.join([
'slice.status.slice_id.contextId.contextUuid.uuid({})',
'is not empty and is different than',
'slice.slice_id.contextId.contextUuid.uuid({}).',
'Optionally, leave field empty to use slice.slice_id.contextId.contextUuid.uuid({}), if set,',
'or, otherwise, the default Context({})'
])
msg = msg.format(
status_context_id, context_id, context_id, DEFAULT_CONTEXT_ID)
raise ServiceException(grpc.StatusCode.INVALID_ARGUMENT, msg)
if (len(status_slice_id) > 0) and (status_slice_id != slice_id):
msg = ' '.join([
'slice.status.slice_id.slice_id.uuid({})',
'is not empty and is different than',
'slice.slice_id.slice_id.uuid({}).',
'Optionally, leave field empty to use slice.slice_id.slice_id.uuid({}).',
])
msg = msg.format(
status_slice_id, slice_id, slice_id)
raise ServiceException(grpc.StatusCode.INVALID_ARGUMENT, msg)
slice_status = check_slice_status(method_name, slice_status)
# ----- Check if slice exists in database --------------------------------------------------------------------------
_check_slice_exists(method_name, database, context_id, slice_id)
# ----- Parse endpoints and check if they exist in the database as device endpoints --------------------------------
db_endpoints__port_types = _check_slice_endpoints(logger, database, context_id, slice_id, request.endpoints)
# ----- Parse constraints ------------------------------------------------------------------------------------------
parent_name = 'Context({})/Slice({})'.format(context_id, slice_id)
constraint_tuples : List[Tuple[str, str]] = check_constraints(logger, parent_name, request.constraints)
# ----- Parse Service Ids ------------------------------------------------------------------------------------------
db_services = _check_services(logger, database, parent_name, context_id, request.services)
# ----- Parse SubSlice Ids -----------------------------------------------------------------------------------------
db_subslices = _check_subslices(logger, database, parent_name, context_id, request.subSlicesId)
return context_id, slice_id, slice_status, db_endpoints__port_types, constraint_tuples, db_services, db_subslices
import logging, os, signal, sys, threading
from prometheus_client import start_http_server
from common.database.Factory import get_database
from slice.service.SliceService import SliceService
from slice.Config import GRPC_SERVICE_PORT, GRPC_MAX_WORKERS, GRPC_GRACE_PERIOD, LOG_LEVEL, METRICS_PORT
terminate = threading.Event()
logger = None
def signal_handler(signal, frame):
global terminate, logger
logger.warning('Terminate signal received')
terminate.set()
def main():
global terminate, logger
service_port = os.environ.get('SLICESERVICE_SERVICE_PORT_GRPC', GRPC_SERVICE_PORT)
max_workers = os.environ.get('MAX_WORKERS', GRPC_MAX_WORKERS )
grace_period = os.environ.get('GRACE_PERIOD', GRPC_GRACE_PERIOD)
log_level = os.environ.get('LOG_LEVEL', LOG_LEVEL )
metrics_port = os.environ.get('METRICS_PORT', METRICS_PORT )
logging.basicConfig(level=log_level)
logger = logging.getLogger(__name__)
signal.signal(signal.SIGINT, signal_handler)
signal.signal(signal.SIGTERM, signal_handler)
logger.info('Starting...')
# Start metrics server
start_http_server(metrics_port)
# Get database instance
database = get_database()
# Starting device service
grpc_service = SliceService(database, port=service_port, max_workers=max_workers, grace_period=grace_period)
grpc_service.start()
# Wait for Ctrl+C or termination signal
while not terminate.wait(timeout=0.1): pass
logger.info('Terminating...')
grpc_service.stop()
logger.info('Bye')
return 0
if __name__ == '__main__':
sys.exit(main())
import copy, grpc, logging, pytest
from common.database.Factory import get_database, DatabaseEngineEnum
from slice.client.SliceClient import SliceClient
from slice.proto.slice_pb2 import TransportSlice
from slice.service.SliceService import SliceService
from slice.Config import GRPC_SERVICE_PORT, GRPC_MAX_WORKERS, GRPC_GRACE_PERIOD
port = 10000 + GRPC_SERVICE_PORT # avoid privileged ports
LOGGER = logging.getLogger(__name__)
LOGGER.setLevel(logging.DEBUG)
@pytest.fixture(scope='session')
def slice_database():
_database = get_database(engine=DatabaseEngineEnum.INMEMORY)
return _database
@pytest.fixture(scope='session')
def slice_service(slice_database):
_service = SliceService(
slice_database, port=port, max_workers=GRPC_MAX_WORKERS, grace_period=GRPC_GRACE_PERIOD)
_service.start()
yield _service
_service.stop()
@pytest.fixture(scope='session')
def slice_client(slice_service):
_client = SliceClient(address='127.0.0.1', port=port)
yield _client
_client.close()
def test_add_device_wrong_attributes(slice_client : SliceClient):
# should fail with slice uuid is empty
with pytest.raises(grpc._channel._InactiveRpcError) as e:
slice_client.CreateUpdateSlice(TransportSlice())
assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT
assert e.value.details() == 'slice.slice_id.slice_id.uuid() string is empty.'
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment